#############
# 参考资料 #
#############
# 《阮一峰的Docker入门教程》
# http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html
# 《Docker — 从入门到实践》
# https://www.bookstack.cn/read/docker_practice-v1.1.0/SUMMARY.md
# https://github.com/yeasy/docker_practice
#############
# 0.基础概念 #
#############
#1. 虚拟机
#虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。
#虽然用户可以通过虚拟机还原软件的原始环境。但是,这个方案有几个缺点。
#(1)资源占用多
#虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。
#(2)冗余步骤多
#虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。
#(3)启动慢
#启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。
#2. Linux 容器
#由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。
#Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。
#由于容器是进程级别的,相比虚拟机有很多优势。
#(1)启动快
#容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。
#(2)资源占用少
#容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。
#(3)体积小
#容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。
#总之,容器有点像轻量级的虚拟机,能够提供虚拟化的环境,但是成本开销小得多。
#3. Docker 是什么?
#Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
#Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
#总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
#Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
#Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
#4. Docker 的用途
#更高效的利用系统资源、更快速的启动时间、一致的运行环境、持续交付和部署、更轻松的迁移、更轻松的维护和扩展
#Docker 的主要用途,目前有三大类。
#(1)提供一次性的环境。比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
#(2)提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
#(3)组建微服务架构。通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。
#5. 镜像(Image)
#Docker 镜像(Image),就相当于是一个 root 文件系统。
#Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
#因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
#镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。
#6. 容器(Container)
#镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
#容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。
#每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为 容器存储层。
#容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
#数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
#7. 仓库(Repository)
#一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。
#以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。
#最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。除此以外,还有 CoreOS 的 Quay.io,CoreOS 相关的镜像存储在这里;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务。
################
# 1.安装Dcoker #
################
#Docker 分为 CE 和 EE 两大版本。CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。
#1. Ubuntu 安装 Docker CE
#添加稳定版本的 Docker CE APT 镜像源
apt-get remove docker docker-engine docker.io #卸载旧版本
apt-get update
apt-get install apt-transport-https ca-certificates curl software-properties-common #由于 apt 源使用 HTTPS 以确保软件下载过程中不被篡改。因此,我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书
curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add - #为了确认所下载软件包的合法性,需要添加软件源的 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add #官方源
add-apt-repository "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable" #向 source.list 中添加 Docker 软件源
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" #官方源
#安装 Docker CE
apt-get update
apt-get install docker-ce
#使用脚本自动安装 Docker CE 的 Edge 版本
curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun
#启动 Docker CE
systemctl enable docker
systemctl start docker
#建立 docker 用户组
groupadd docker #建立 docker 组
usermod -aG docker $USER #将当前用户加入 docker 组
#测试 Docker 是否安装正确
docker run hello-world
#2. CentOS 安装 Docker CE
#https://docs.docker.com/engine/install/centos/
#使用 yum 安装
yum list installed | grep docker #查看安装了那些docker包
yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine #卸载docker旧版本
yum install -y yum-utils device-mapper-persistent-data lvm2 #安装相关工具类
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo #配置docker仓库,阿里云源
yum-config-manager --add-repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo #中科大源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo #官方源
#安装 Docker CE
yum makecache fast #快速更新 yum 软件源缓存
yum install docker-ce #安装docker引擎
#使用脚本自动安装
curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun
#启动 Docker CE
docker version #验证是否安装成功
systemctl enable docker
systemctl start docker #开启docker
#建立 docker 用户组
groupadd docker #建立 docker 组
usermod -aG docker $USER #将当前用户加入 docker 组
#测试 Docker 是否安装正确
docker run hello-world
#3. 配置代理
#问题1:systemctl start docker-Job for docker.service failed because the control process exited with error code.
#解答1:一般是由daemon文件格式编写出错引起的
#查询docker错误信息的几条命令
sudo systemctl status docker.service
sudo journalctl -fu docker.service
cat /var/log/daemon.log | grep docker
cat /var/log/messages | grep docker
vi /etc/docker/daemon.json
#配置daemon.json文件
cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://dockerhub.azk8s.cn",
"https://hub-mirror.c.163.com"
],
"insecure-registries":["rnd-dockerhub.huawei.com"]
}
EOF
systemctl daemon-reload #使daemon.json生效
systemctl restart docker #重启docker
systemctl daemon-reload && systemctl restart docker #重启docker服务
docker info #查看docker配置
docker run hello-world #验证docker是否安装成功
#问题2:docker run hello-world-Error response from daemon: Get https://registry-1.docker.io/v2/: dial tcp: lookup registry-1.docker.io
#解决2:一般是代理配置有问题
#配置docker代理
Environment="HTTP_PROXY=http://<user>:<pass>@ip:port"
Environment="HTTPS_PROXY=http://<user>:<pass>@ip:port"
mkdir -p /etc/systemd/system/docker.service.d
cd /etc/systemd/system/docker.service.d
touch https-proxy.conf
cat > /etc/systemd/system/docker.service.d/https-proxy.conf << EOF
[Service]
Environment="HTTP_PROXY=http://z00575241:zch19950329%2a@90.253.74.198:6688"
Environment="HTTPS_PROXY=http://z00575241:zch19950329%2a@90.253.74.198:6688"
Environment="NO_PROXY=localhost,127.0.0.1,.huawei.com"
EOF
#修改DNS
cat >> /etc/resolv.conf << EOF
nameserver 8.8.8.8
nameserver 8.8.4.4
nameserver 223.5.5.5
nameserver 223.6.6.6
EOF
#修改/etc/sysconfig/docker
vi /etc/sysconfig/docker
OPTIONS='--selinux-enabled --log-driver=journald --registry-mirror=https://docker.mirrors.ustc.edu.cn'
#修改/etc/sysconfig/selinux
vi /etc/sysconfig/selinux
selinux-enabled=false
systemctl daemon-reload && systemctl restart docker
systemctl show --property=Environment docker
docker run hello-world
#拉取镜像并启动容器
docker pull centos:centos7.6.1810 #拉取基础镜像
docker tag centos centos:7.6 #为镜像设置tag
docker run -it centos:7.6 /bin/bash #启动容器
#############
# 2.使用镜像 #
#############
#1. 获取镜像
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
Docker 镜像仓库地址 #地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
仓库名 #如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
docker pull ubuntu:18.04
docker pull registry-cbu.huawei.com/euleros_docker/euleros_x86_64:2.9.3 #EulerOS_Server_V200R009C00SPC103B230
docker pull registry-cbu.huawei.com/euleros_docker/euleros_arm:2.9.3 #EulerOS_Server_V200R009C00SPC103B230
#2. 运行容器
docker run -it --rm ubuntu:18.04 bash
-it #这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
--rm #这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
ubuntu:18.04 #这是指用 ubuntu:18.04 镜像为基础来启动容器。
bash #放在镜像名后的是 命令,这里我们希望有个交互式 Shell,因此用的是 bash。
exit
#3. 列出镜像
docker image ls #会列出所有顶层镜像
docker image ls -a #显示包括中间层镜像在内的所有镜像
docker image ls ubuntu
docker image ls ubuntu:18.04
docker image ls -f since=mongo:3.2 #希望看到在 mongo:3.2 之后建立的镜像
docker image ls -f before=mongo:3.2 #希望看到在 mongo:3.2 之前建立的镜像
docker image ls -f label=com.example.version=0.1 #以通过 LABEL 来过滤
docker system df #查看镜像、容器、数据卷所占用的空间
docker image ls -f dangling=true #无标签镜像也被称为 虚悬镜像(dangling image) ,用此命令专门显示这类镜像
docker image prune #删除虚悬镜像
docker image ls -q
docker image ls --format "{{.ID}}: {{.Repository}}" #直接列出镜像结果,并且只包含镜像ID和仓库名
docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" #以表格等距显示,并且有标题行
#4. 删除镜像
#所以并非所有的 docker image rm 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已
#当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为
docker rmi -f 23c146def05d
docker image rm 501
docker image rm centos
docker image ls --digests
docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
docker image rm $(docker image ls -q redis) #删除所有仓库名为 redis 的镜像
docker image rm $(docker image ls -q -f before=mongo:3.2) #删除所有在 mongo:3.2 之前的镜像
#############
# 3.制作镜像 #
#############
#1. 利用 commit 理解镜像构成
#使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用
#使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为 黑箱镜像
#2. 使用 Dockerfile 定制镜像
#镜像的定制实际上就是定制每一层所添加的配置、文件
#Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建
#Docker 存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像
#如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在
#Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层
#在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建
#Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以及行首 # 进行注释的格式
#因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉
FROM #指定基础镜像
RUN #执行命令
RUN <命令> #shell 格式
RUN ["可执行文件", "参数1", "参数2"] #exec 格式
FROM debian:stretch
RUN buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
#docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的
#这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
#一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
docker build [选项] <上下文路径/URL/->
#使用 Dockerfile 定制镜像
docker build -t nginx:v3 . #"."表示上下文路径
#直接用 Git repo 进行构建
docker build https://github.com/twang2218/gitlab-ce-zh.git#:11.1
#用给定的 tar 压缩包构建
docker build http://server/context.tar.gz
#从标准输入中读取 Dockerfile 进行构建
docker build - < Dockerfile
#从标准输入中读取上下文压缩包进行构建
docker build - < context.tar.gz
#3. Dockerfile 指令详解
#3.1 COPY 复制文件
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
COPY package.json /usr/src/app/ #COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
COPY hom* /mydir/
COPY hom?.txt /mydir/
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/
#3.2 ADD 更高级的复制文件
#所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz / #如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去
ADD --chown=55:mygroup files* /mydir/
ADD --chown=bin files* /mydir/
ADD --chown=1 files* /mydir/
ADD --chown=10:11 files* /mydir/
#3.3 CMD 容器启动命令
CMD <命令> #shell 格式
CMD ["可执行文件", "参数1", "参数2"...] #exec 格式
CMD echo $HOME ==> CMD [ "sh", "-c", "echo $HOME" ] #如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行
CMD ["nginx", "-g", "daemon off;"]
#3.4 ENTRYPOINT 入口点
#ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数
#场景一:让镜像变成像命令一样使用
#因为当存在 ENTRYPOINT 后,CMD 的内容将会作为参数传给 ENTRYPOINT,而这里 -i 就是新的 CMD,因此会作为参数传给 curl,从而达到了我们预期的效果
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "https://ip.cn" ]
#场景二:应用运行前的准备工作
FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD [ "redis-server" ]
#E3.5 NV 设置环境变量
#下列指令可以支持环境变量展开: ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
#3.6 ARG 构建参数
#构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的
#Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖
ARG <参数名>[=<默认值>]
#3.7 VOLUME 定义匿名卷
#为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据
#这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
VOLUME /data
docker run -d -v mydata:/data xxxx #运行时可以覆盖这个挂载设置
#3.8 EXPOSE 声明端口
#EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务
#要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射
EXPOSE <端口1> [<端口2>...]
#3.9 WORKDIR 指定工作目录
#使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录
#因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令
WORKDIR <工作目录路径>
#3.10 USER 指定当前用户
USER <用户名>[:<用户组>]
RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]
# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下载 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true
# 设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]
#3.11 HEALTHCHECK 健康检查
#HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常
#和 CMD, ENTRYPOINT 一样,HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效
HEALTHCHECK [选项] CMD <命令> #设置检查容器健康状况的命令
HEALTHCHECK NONE #如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1
#3.12 ONBUILD 为他人做嫁衣裳
#ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行
#Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的
ONBUILD <其它指令>
FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]
#4. Dockerfile 多阶段构建
#4.1 全部放入一个 Dockerfile
#镜像层次多,镜像体积较大,部署时间变长、源代码存在泄露的风险
FROM golang:1.9-alpine
RUN apk --no-cache add git ca-certificates
WORKDIR /go/src/github.com/go/helloworld/
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root
WORKDIR /root/
CMD ["./app"]
$ docker build -t go/helloworld:1 -f Dockerfile.one .
#4.2 分散到多个 Dockerfile
#部署过程较复杂
#!/bin/sh
echo Building go/helloworld:build
docker build -t go/helloworld:build . -f Dockerfile.build
docker create --name extract go/helloworld:build
docker cp extract:/go/src/github.com/go/helloworld/app ./app
docker rm -f extract
echo Building go/helloworld:2
docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
rm ./app
#4.3 使用多阶段构建
FROM golang:1.9-alpine as builder
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]
$ docker build -t go/helloworld:3 .
#4.4 只构建某一阶段的镜像
#4.5 构建时从其他镜像复制文件
#4.6 构建多种系统架构支持的 Docker 镜像 — docker manifest 命令详解
#4.7 使用 BuildKit 构建镜像
#4.8 从 rootfs 压缩包导入
docker import http://download.openvz.org/template/precreated/ubuntu-16.04-x86_64.tar.gz openvz/ubuntu:16.04 #创建一个 OpenVZ 的 Ubuntu 16.04 模板的镜像
#4.9 docker save 和 docker load
docker image ls alpine
docker save alpine -o <filename>
file <filename> #输出文件类型
docker save alpine | gzip > alpine-latest.tar.gz #使用 gzip 压缩
docker load -i alpine-latest.tar.gz #加载镜像
docker save <镜像名> | bzip2 | pv | ssh <用户名>@<主机名> 'cat | docker load' #如果我们结合这两个命令以及 ssh 甚至 pv 的话,利用 Linux 强大的管道,我们可以写一个命令完成从一个机器将镜像迁移到另一个机器,并且带进度条的功能
#############
# 3.实际操作 #
#############
#1. 操作容器
#2. 访问仓库
#3. 数据管理
#4. 使用网络
#5. 高级网络配置
#################
# 4.Docker三剑客 #
#################
#1. Comopose项目
#2. Machine项目
#3. Docker Swarm
#############
# 5.底层实现 #
#############
#############
# 6.项目实战 #
##############
#2. image 文件
#Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。
#image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成。
docker image ls #列出本机的所有 image 文件
docker image rm [imageName] #删除 image 文件
docker tag centos:7.6.1810 centos7.6:latest #将centos:7.6.1810修改为centos7.6:latest,原先的centos:7.6.1810仍旧存在,需要将其删除
docker rmi -f centos:centos7.6.1810 #删除centos的tag-centos7.6.1810
#3. 实例:hello world
docker image pull library/hello-world #docker image pull hello-world
docker container run hello-world #docker container run命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。输出提示以后,hello world就会停止运行,容器自动终止。
docker container run -it ubuntu bash
docker container kill [containID] #对于那些不会自动终止的容器,必须使用docker container kill 命令手动终止
–detach -d #在后台运行容器,并且打印容器id。
–interactive -i #即使没有连接,也要保持标准输入保持打开状态,一般与 -t 连用。
–tty -t #分配一个伪tty,一般与 -i 连用。
#4. 容器文件
#image 文件生成的容器实例,本身也是一个文件,称为容器文件。也就是说,一旦容器生成,就会同时存在两个文件: image 文件和容器文件。而且关闭容器并不会删除容器文件,只是容器停止运行而已。
docker container ls #列出本机正在运行的容器
docker container ls --all #列出本机所有容器,包括终止运行的容器
docker container rm [containerID] #终止运行的容器文件,依然会占据硬盘空间,可以使用docker container rm命令删除
#5. Dockerfile文件
#这就需要用到 Dockerfile 文件。它是一个文本文件,用来配置 image。Docker 根据 该文件生成二进制的 image 文件。
#############
# 2.制作容器 #
#############
#1. 编写 Dockerfile 文件
git clone https://github.com/ruanyf/koa-demos.git
#首先,在项目的根目录下,新建一个文本文件.dockerignore,写入下面的内容。代码表示,这三个路径要排除,不要打包进入 image 文件。
.git
node_modules
npm-debug.log
#然后,在项目的根目录下,新建一个文本文件 Dockerfile,写入下面的内容。
FROM node:8.4 #该 image 文件继承官方的 node image,冒号表示标签,这里标签是8.4,即8.4版本的 node。
COPY . /app #将当前目录下的所有文件(除了.dockerignore排除的路径),都拷贝进入 image 文件的/app目录。
WORKDIR /app #指定接下来的工作路径为/app。
RUN npm install #在/app目录下,运行npm install命令安装依赖。注意,安装后所有的依赖,都将打包进入 image 文件。
EXPOSE 3000 #将容器 3000 端口暴露出来, 允许外部连接这个端口。
#2. 创建 image 文件
#-t参数用来指定 image 文件的名字,后面还可以用冒号指定标签。如果不指定,默认的标签就是latest。最后的那个点表示 Dockerfile 文件所在的路径,上例是当前路径,所以是一个点。
docker image build -t koa-demo . #docker image build -t koa-demo:0.0.1,有了 Dockerfile 文件以后,就可以使用docker image build命令创建 image 文件了。
#docker image build -t koa-demo . -问题:npm ERR! network request to https://registry.npm.taobao.org/delegates failed, reason: socket hang up
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm install nrm -g
#3. 生成容器
#-i 选项指示 docker 要在容器上打开一个标准的输入接口,-t 指示 docker 要创建一个伪 tty 终端,连接容器的标准输入接口,之后用户就可以通过终端进行输入。由于 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 命令的默认 COMMAND 为 /bin/bash,因此用户的输入是基于 bash shell 执行的。
docker container run -p 8000:3000 -it koa-demo /bin/bash #docker container run -p 8000:3000 -it koa-demo:0.0.1 /bin/bash
-p参数 #容器的 3000 端口映射到本机的 8000 端口。
-it参数 #容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器。
koa-demo:0.0.1 #image 文件的名字(如果有标签,还需要提供标签,默认是 latest 标签)。
/bin/bash #容器启动以后,内部第一个执行的命令。这里是启动 Bash,保证用户可以使用 Shell。
docker container run --rm -p 8000:3000 -it koa-demo /bin/bash #以使用docker container run命令的--rm参数,在容器终止运行后自动删除容器文件
#4. CMD命令
#RUN命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件
#CMD命令则是在容器启动后执行
#一个 Dockerfile 可以包含多个RUN命令,但是只能有一个CMD命令
docker container run --rm -p 8000:3000 -it koa-demo:0.0.1
#5. 发布 image 文件
docker login
docker image tag [imageName] [username]/[repository]:[tag] #为本地的 image 标注用户名和版本
docker image tag koa-demos:0.0.1 ruanyf/koa-demos:0.0.1
docker image build -t [username]/[repository]:[tag] . #也可以不标注用户名,重新构建一下 image 文件
docker image push [username]/[repository]:[tag] #发布 image 文件
#############
# 3.常用命令 #
#############
docker container run [containerID] #新建容器,每运行一次,就会新建一个容器
docker container start [containerID] #用来启动已经生成、已经停止运行的容器文件
docker container kill [containerID] #终止容器运行,相当于向容器里面的主进程发出 SIGKILL 信号
docker container stop [containerID] #相当于向容器里面的主进程发出 SIGTERM 信号,然后过一段时间再发出 SIGKILL 信号
docker container logs [containerID] #用来查看 docker 容器的输出,即容器里面 Shell 的标准输出。如果docker run命令运行容器的时候,没有使用-it参数,就要用这个命令查看输出。
docker container exec -it [containerID] /bin/bash #用于进入一个正在运行的 docker 容器。如果docker run命令运行容器的时候,没有使用-it参数,就要用这个命令进入容器。
docker container cp [containID]:[/path/to/file] . #用于从正在运行的 Docker 容器里面,将文件拷贝到本机