「docker」学习笔记 | 镜像、容器、仓库
关于docker
1. why docker?
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows机器上,从而实现虚拟化应用,docker这扇窗打开后就相当于另一个github。容器是完全使用沙箱机制,容器之间不会有任何接口。
📖 官方文档:https://docs.docker.com/
2. docker架构
*转自拉勾教育《由浅入深吃透Docker》
在了解 Docker 架构前,我先说下相关的背景知识——容器的发展史。
容器技术随着 Docker 的出现变得炙手可热,所有公司都在积极拥抱容器技术。此时市场上除了有 Docker 容器,还有很多其他的容器技术,比如 CoreOS 的 rkt、lxc 等。容器技术百花齐放是好事,但也出现了很多问题。比如容器技术的标准到底是什么?容器标准应该由谁来制定?
也许你可能会说, Docker 已经成为了事实标准,把 Docker 作为容器技术的标准不就好了?事实并没有想象的那么简单。因为那时候不仅有容器标准之争,编排技术之争也十分激烈。当时的编排技术有三大主力,分别是 Docker Swarm、Kubernetes 和 Mesos 。Swarm 毋庸置疑,肯定愿意把 Docker 作为唯一的容器运行时,但是 Kubernetes 和 Mesos 就不同意了,因为它们不希望调度的形式过度单一。
在这样的背景下,最终爆发了容器大战,OCI也正是在这样的背景下应运而生。
OCI全称为开放容器标准(Open Container Initiative),它是一个轻量级、开放的治理结构。OCI组织在 Linux 基金会的大力支持下,于 2015 年 6 月份正式注册成立。基金会旨在为用户围绕工业化容器的格式和镜像运行时,制定一个开放的容器标准。目前主要有两个标准文档:容器运行时标准 (runtime spec)和容器镜像标准(image spec)。
正是由于容器的战争,才导致 Docker 不得不在战争中改变一些技术架构。最终形成了下图所示的技术架构。
/Docker 架构图/
我们可以看到,Docker 整体架构采用 C/S(客户端 / 服务器)模式,主要由客户端和服务端两大部分组成。客户端负责发送操作指令,服务端负责接收和处理指令。客户端和服务端通信有多种方式,既可以在同一台机器上通过UNIX套接字通信,也可以通过网络连接远程通信。
-
Docker Client
- 客户端Docker 客户端其实是一种泛称。其中 docker 命令是 Docker 用户与 Docker 服务端交互的主要方式。除了使用 docker 命令的方式,还可以使用直接请求 REST API 的方式与 Docker 服务端交互,甚至还可以使用各种语言的 SDK 与 Docker 服务端交互。目前社区维护着 Go、Java、Python、PHP 等数十种语言的 SDK,足以满足你的日常需求。
-
Docker Daemon
- 守护进程,即服务器端Docker 服务端是 Docker 所有后台服务的统称。其中 dockerd 是一个非常重要的后台管理进程,它负责响应和处理来自 Docker 客户端的请求,然后将客户端的请求转化为 Docker 的具体操作。例如镜像、容器、网络和挂载卷等具体对象的操作和管理。
Docker 从诞生到现在,服务端经历了多次架构重构。起初,服务端的组件是全部集成在 docker 二进制里。但是从 1.11 版本开始, dockerd 已经成了独立的二进制,此时的容器也不是直接由 dockerd 来启动了,而是集成了 containerd、runC 等多个组件。
虽然 Docker 的架构在不停重构,但是各个模块的基本功能和定位并没有变化。它和一般的 C/S 架构系统一样,Docker 服务端模块负责和 Docker 客户端交互,并管理 Docker 的容器、镜像、网络等资源。
3. 四大核心概念
Docker Images
(镜像):Docker Images是一个只读模板,它包含创建 Docker容器的说明。它和系统安装光盘有点像(或者面向对象中的类),使用系统安装光盘可以安装系统,同理,使用 Docker镜像可以运行 Docker镜像中的程序Docker Container
(容器):Docker Container是镜像的可运行实例(面向对象中的实例化对象)镜像和容器的关系有点类似于面向对象中,类和对象的关系。可通过 Docker API或者CL命令来启停、移动、删除容器Docker Repository
(仓库):Docker 的镜像仓库类似于代码仓库,用来存储和分发 Docker 镜像。镜像仓库分为公共镜像仓库和私有镜像仓库。镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,这时候就用到了仓库。目前,Docker Hub 是 Docker 官方的公开镜像仓库,它不仅有很多应用或者操作系统的官方镜像,还有很多组织或者个人开发的镜像供我们免费存放、下载、研究和使用。除了公开镜像仓库,你也可以构建自己的私有镜像仓库Docker Registry
(注册服务器):Docker Registry是一个「集中存储」与「分发镜像」的服务,相当于一个仓库。注册服务器是存放仓库的实际服务器,而仓库则可以被理解为一个具体的项目或者目录;注册服务器可以包含很多个仓库,每个仓库又可以包含多个镜像。Docker Registry可分为公有 Docker Registry和私有Docker Registry。最常用的 Docker Registry是官网的Docker Hub,这也是默认的 Docker Registry。Docker Hub上存放若大量优秀的镜像,可使用 Docker命令下载并使用
/关系图/
/镜像、容器、仓库/
/registry、repository、image/
4. 开发人员和生产环境为什么要使用 Docker?
作为一种新兴的虚拟化方式,Docker跟传统的虚拟化方式相比具有众多的优势。
- Docker容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多
- Docker对系统资源的利用率很高,一台主机上可同时运行数千个 Docker容器
- 容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销小,比虚拟机节省内存,启动更快
- 传统虚拟机方式运行10个不同的应用就要起10个虚拟机,而 Docker只需要启动10个隔离的应用即可,快速部署,安全运行,不污染系统
/docker与虚拟机的区别/
5. 感言
- Docker的架构很有魅力,拥有类似于虚拟机性质的隔离机制,但并不是严格意义上的虚拟机。以前我们是一条小船运一个集装箱的货物,现在可以把N个集装箱扔到一条大货轮上。每个容器(集装箱)共用宿主机(货轮)的内核(运载力),Docker file文件就像是每个集装箱中的货物清单和说明书,Docker compose软件就像装卸一系列集装箱的流程和标准
- 用了Docker方才觉得生产环境终于有了该有的样子,就像集装箱普及之后,大型货轮的价值才逐渐体现出来。
- 为了生产的编程就应该拥抱搭积木式的架构,开箱即用,隔离清晰,高复用,低耦合。
总结:如果你也觉得虚拟机太笨重了,那么docker就是它的不二替代品! 🤥
安装docker
1. CentOS
sudo yum remove docker docker-common docker-selinux docker-engine
- 卸载旧版本的软件sudo yum install -y yum-utils device-mapper-persistent-data lvm2
- 安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的yum list docker-ce --showduplicates | sort -r
- 查看所有仓库中所有docker版本,并选择特定版本安装sudo yum install docker-ce
- 由于repo中默认只开启stable仓库,故这里安装的是最新稳定版17.12.0sudo systemctl start docker
- 启动dockersudo systemctl enable docker
- 设置开机自启动docker version
- 查看版本- 安装
docker-compose
- https://www.jianshu.com/p/5ba9f9159696
2. macOS
-
下载离线安装包
-
安装完输入命令检查是否成功:
docker --version docker-compose --version docker-machine --version
-
设置docker自启动
systemctl enable docker
-
docker的启动、停止、重启
service docker start/stop/restart 或 systemctl start/stop/restart docker
-
查看docker的运行状态
systemctl status docker
3. Windows
镜像(images)管理
镜像的基本操作
查看本地镜像 | docker images |
查找镜像 | docker search image_name |
拉取镜像 | docker pull [OPTIONS] NAME[:TAG] |
推送镜像 | docker push [OPTIONS] NAME[:TAG] |
查看镜像信息 | docker inspect image_name/image_id |
删除镜像 | docker rmi image_name/image_id |
重命名镜像 | docker tag image_name |
运行镜像(交互方式) | docker run -it image_name /bin/sh |
删除所有镜像 | docker rmi docker images -q |
ps:docker pull ubuntu
或docker pull ubuntu:latest
这两个命令会拉取最新版本的ubuntu镜像,另外也可以指定想要的镜像版本进行拉取,拉取指定版本的镜像:
- Ubuntu:
docker pull ubuntu:16.04
- Centos:
docker pull centos:centos6.8
- mysql:
docker pull mysql:5.7
已拉取的镜像保存位置:
/var/lib/docker/image/overlay2/repositories.json
hello-world
学习编程语言的第一个示例都是Hello Wolrd
,Docker 也不例外,运行 docker run hello-world
验证一下吧(该命令下 Docker 会在本地查找镜像是否存在,之后 Docker 会去远程 Docker registry server 下载这个镜像),以下为运行信息:
阅读运行信息,可以得知执行docker run ...
命令之后docker所完成的工作,即连接到docker守护进程、拉取镜像、创建一个容器blablabla…
容器(container)管理
容器的基本操作
查看本地容器 | docker ps -a (-q) / docker container ls -a |
新建一个容器 | docker create -it –name=Name Image |
新建一个容器(运行模式) | docker run –name Name Image |
查看容器信息 | docker inspect Name/ID |
启动/停止/重启容器 | docker start/stop/restart Name/ID |
进入容器操作 | docker exec -it Name/ID /bin/bash |
暂停/强制停止容器 | docker pause/kill Name/ID |
删除容器,删除所有容器 | docker rm Name/ID,docker rm $(docker ps -a -q) |
输出容器日志 | docker logs Name/ID |
查看容器中运行的进程 | docker top Name/ID |
显示容器资源使用情况 | docker stats |
检查容器里文件结构的更改 | docker diff Name/ID |
导出容器到文件 | docker export Name/ID > filename |
导入容器 | docker import filename Name/ID |
将容器打包成镜像 | docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] |
/关系图/
详细参数解析
-d, –detach=false | 指定容器运行于前台还是后台,默认为false |
-i, –interactive=false | 打开STDIN,用于控制台交互 |
-t, –tty=false | 分配tty设备,该可以支持终端登录,默认为false |
-u, –user="" | 指定容器的用户 |
-a, –attach=[] | 标准输入输出流和错误信息(必须是以非docker run -d启动的容器) |
-w, –workdir="" | 指定容器的工作目录 |
-c, –cpu-shares=0 | 设置容器CPU权重,在CPU共享场景使用 |
-e, –env=[] | 指定环境变量,容器中可以使用该环境变量 |
-m, –memory="" | 指定容器的内存上限 |
-p, –publish=[] | 指定容器暴露的端口 |
-h, –hostname="" | 指定容器的主机名 |
-v, –volume=[] | 给容器挂载存储卷,挂载到容器的某个目录 |
–volumes-from=[] | 给容器挂载宿主机或其他容器上的卷,挂载到容器的某个目录或文件。可以绑定文件,也可以绑定文件夹 |
–cap-add=[] | 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities |
–cap-drop=[] | 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities |
–cidfile="" | 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法 |
–cpuset="" | 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU |
–device=[] | 添加主机设备给容器,相当于设备直通 |
–dns=[] | 指定容器的dns服务器 |
–dns-search=[] | 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件 |
–entrypoint="" | 覆盖image的入口点 |
–env-file=[] | 指定环境变量文件,文件格式为每行一个环境变量 |
–expose=[] | 指定容器暴露的端口,即修改镜像的暴露端口 |
–link=[] | 指定容器间的关联,使用其他容器的IP、env等信息 |
–lxc-conf=[] | 指定容器的配置文件,只有在指定–exec-driver=lxc时使用 |
–name="" | 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字 |
–net=“bridge” | 容器网络设置:bridge //使用docker daemon指定的网桥;host //容器使用主机的网络; container:NAME_or_ID //使用其他容器的网路,共享IP和PORT等网络资源;none //容器使用自己的网络(类似–net=bridge),但是不进行配置; |
–privileged=false | 指定容器是否为特权容器,特权容器拥有所有的capabilities |
–restart=“no” | 指定容器停止后的重启策略:no:容器退出时不重启; on-failure:容器故障退出(返回值非零)时重启; always:容器退出时总是重启 |
–rm=false | 指定容器停止后自动删除容器(不支持以docker run -d启动的容器) |
–sig-proxy=true | 设置由代理接受并处理信号,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理 |
(1)创建并启动容器
两种方式:
-
docker create ...
+docker start ...
创建容器,启动容器。
例: 创建Ubuntu容器:
docker create -it --name=ubuntu16 ubuntu:16.04
-
创建之前需确认使用的Ubuntu镜像版本,使用命令
docker images -a
或docker ps -a
-
-i
- 打开STDIN -
-t
- 分配一个伪终端,同时使用 -it 参数可以让我们进入交互模式 -
ubuntu:16.04
- 这是使用到的镜像,需指定版本号,如不指定版本号,则默认选取latest版本的镜像 -
--name=ubuntu16
- 为创建的容器命名为ubuntu16,注意是两个--
-
-
docker run ...
使用
docker run
命令直接基于镜像新建一个容器并启动,一气呵成。相当于先执行docker create
命令从镜像创建容器,然后再执行docker start
命令启动容器。当使用docker run创建并启动容器时,Docker 后台执行的流程为:
-
Docker 会检查本地是否存在镜像,如不存在则从 Docker Hub 拉取镜像
-
使用镜像创建一个容器
-
分配文件系统,并且在镜像只读层外创建一个读写层
-
从 Docker IP 池中分配一个 IP 给容器
-
执行启动命令运行镜像
例:运行nginx容器:
docker run --name mynginx -p 8080:80 -v /home/nginx/logs:/var/log/nginx:v1 -d nginx
-name
:容器名称,自主命名-p 8080:80
:设置端口映射,将宿主机的8080端口映射发到容器内部的80端口-d
:设置容器在后台一直运行nginx
:使用的镜像-v /home/nginx/logs:/var/log/nginx:rw
🤪- docker 启动容器时,如果不使用 -v/–volume 挂载宿主机的文件或文件夹,容器内的配置文件只能进入到容器内才能修改,输入的日志也是在容器里查看;这样不方便修改配置,也不利于日志查看;所以一般都是进行 -v 参数来进行挂载文件或文件夹
- 三种模式:rw、ro 和 不指定
- 不指定(默认)。即不设置
-v
指令,容器或宿主机文件修改,双方都互不影响 rw
(read-write)- 文件:不管是宿主机还是容器内修改,都会相互同步;但容器内不允许删除,会提示Device or resource busy;宿主机删除文件,容器内的不会被同步
- 文件夹:不管是宿主机还是容器内修改、新增、删除文件,都会相互同步
ro
(read-only)- 文件:容器内不能修改,会提示read-only
- 文件夹:容器内不能修改、新增、删除文件夹中的文件,会提示read-only
- 不指定(默认)。即不设置
-
(2)启动/终止/重启 容器
docker start/stop/restart name/ID
(3)进入容器
处于运行状态的容器可以通过docker attach、docker exec、nsenter等多种方式进入容器。
-
docker attach name
- 不够灵活,通常使用exec方法进入容器 -
docker exec ...
例:进入命名为myubuntu的容器:
docker exec -it myubuntu -d /bin/bash
-i
:保持STDIN打开-t
:分配一个伪终端-d
:分离模式(在后台运行)
需要退出容器的时候可以执行以下操作:
- exit
- ctrl+d 退出容器且关闭
- ctrl+p+q 退出容器但不关闭
(4)删除容器
如果要删除一个停止状态的容器,可以使用docker rm
命令删除。如果要删除正在运行中的容器,必须添加 -f (或 --force)
参数, Docker 会发送 SIGKILL 信号强制终止正在运行的容器。
docker rm id
# 暴力删除所有的容器
docker rm -f $(docker ps -aq)
# 删除按条件搜索到的容器
docker rm $(docker ps -a|grep rpa|awk '{print $1}')
(5)导出导入容器
1. 导出容器
我们可以使用docker export CONTAINER
命令导出一个容器到文件,不管此时该容器是否处于运行中的状态。执行以下命令后会在当前文件夹下生成 busybox.tar 文件,我们可以将该文件拷贝到其他机器上,通过导入命令实现容器的迁移。
docker export busybox > busybox.tar
2. 导入容器
通过docker export
命令导出的文件,可以使用docker import
命令导入,执行完docker import
后会变为本地镜像,最后再使用docker run
命令启动该镜像,这样我们就实现了容器的迁移。
导入容器的命令格式为 docker import [OPTIONS] file|URL [REPOSITORY[:TAG]]
。接下来我们一步步将上一步导出的镜像文件导入到其他机器的 Docker 中并启动它。
首先,使用docker import
命令导入上一步导出的容器
docker import busybox.tar busybox:test
此时,busybox.tar 被导入成为新的镜像,镜像名称为 busybox:test
(6)容器与宿主机互传文件
首先确定docker容器的ID或NAME,可以使用docker ps -a
或docker images -a
查看
-
容器 -> 本机
docker cp container_id:docker容器内的文件全路径 本机保存文件的全路径
例如:
docker cp 4a2f08d2c1f8:/data1/configure.txt E:\PHP\configure.txt
-
本机 -> 容器
docker cp 本机保存文件的全路径 container_id:docker容器内的文件全路径
例如:
docker cp E:\PHP\configure.txt 4a2f08d2c1f8:/data1/configure.txt
(7)给容器添加或追加端口映射
-
新建容器时,使用docker run命令创建并运行一个容器,如需要添加端口映射则可以通过 -p 参数进行设置,如
docker run -itd --name my_nginx -p 8080:80 nginx
,即可将宿主机的8080端口与容器的80端口进行映射。- 映射一个端口
docker run -itd -p 8080:80
- 映射多个端口
docker run -itd -p 8088:80 -p 8088:80 nginx
- 映射到指定端口的指定地址
docker run -itd -p 39.108.109.60:8080:80 nginx
- 映射到指定端口的任意地址
docker run -itd -p 39.108.109.60::80 nginx
- 查看端口的映射情况
docker port 容器名
- 映射一个端口
-
容器已创建时,如若需要给予容器以更多的端口映射,则可以通过以下三种方式实现
-
配置防火墙转发端口
-
(靠谱)把容器打包成镜像,然后以该镜像为基础新建一个容器
docker commit ...
(8)容器 -> 镜像
-
修改容器配置文件的方式进行修改
-
(1)停止docker服务 -
service docker stop \ systemctl stop docker
-
(2)修改端口配置 -
hostconfig.json
/var/lib/docker/containers/[hash_of_the_container]/hostconfig.json
-
hash_of_the_container
可通过
docker inspect name/ID
查看该容器的"Id"值,Id 就是 容器的 hash 数值。或通过
docker inspect name/ID|grep Id
搜索查看: -
hostconfig.json
在
hostconfig.json
里有"PortBindings":{}
这个配置项,可以改成"PortBindings":{"80/tcp":[{"HostIp":"","HostPort":"8080"}]}
这里 80 是容器端口, 8080 是本地端口,层级关系如下:"PortBindings":{ "22/tcp":[ {"HostIp":"","HostPort":"24"}], "80/tcp":[ {"HostIp":"","HostPort":"8080"}] }
"PortBindings":{"22/tcp":[{"HostIp":"","HostPort":"24"}],"80/tcp":[{"HostIp":"","HostPort":"8080"}]},
-
-
(3)修改端口配置 -
config.v2.json
/var/lib/docker/containers/[hash_of_the_container]/config.v2.json
在ExposedPorts中加上要暴露的端口"ExposedPorts": { "888/tcp": {}, "21/tcp": {} } ... "Ports": { "888/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "888" } ], "21/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "21" } ] }
-
(4)启动docker服务 -
service docker start \ systemctl start docker
-
-
(8)容器 -> 镜像
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
-
[OPTIONS]
- 参数,选项如下:Name, shorthand Default Description --author
,-a
Author (e.g., “John Hannibal Smith hannibal@a-team.com”) --change
,-c
Apply Dockerfile instruction to the created image --message
,-m
Commit message --pause
,-p
true
Pause container during commit -
CONTAINER
- 容器命名或ID -
[REPOSITORY[:TAG]]
- 镜像仓库名:版本号
示例:
docker commit -a "Billie" -m "first version" server_script server_script:v1
-a "Billie"
- 作者,Billie-m "first version"
- 打包信息server_script
- 容器名server_script:v1
- 镜像名:tag
docker commit --change "ENV DEBUG=true" c3f279d17e0a svendowideit/testimage:version3
仓库(repository)管理
关于「仓库」概念的解释请移步 - /note/docker1/#四大核心概念
一句话概括就是,仓库(Repository)是存储和分发 Docker 镜像的地方。
按照类型,我们将镜像仓库分为 公共镜像仓库 和 私有镜像仓库。
1. 公共镜像仓库
公共镜像仓库一般是 Docker 官方或者其他第三方组织(阿里云,腾讯云,网易云等)提供的,允许所有人注册和使用的镜像仓库。
Docker Hub 是全球最大的镜像市场,目前已经有超过 10w 个容器镜像,这些容器镜像主要来自软件供应商、开源组织和社区。大部分的操作系统镜像和软件镜像都可以直接在 Docker Hub 下载并使用 - https://hub.docker.com/
使用docker pull [OPTIONS] NAME[:TAG|@DIGEST]
命令就可以从共有镜像仓库拉取需要的镜像。
2. 私有镜像仓库
-
注册Docker Hub账号 - Docker Hub
-
创建自己的仓库 - https://hub.docker.com/repository/create
-
本地登录镜像服务器 -
docker login
docker login
命令默认会请求 Docker Hub,如果你想登录第三方镜像仓库或者自建的镜像仓库,在docker login
后面加上注册服务器即可。 -
重命名镜像 -
docker tag
docker tag blog:v1 用户名/blog:v1
-
本地推送镜像至仓库 -
docker push
docker push 用户名/blog:v1
-
over
使用 Dockerfile 构建项目
使用 Dockerfile 构建镜像具有以下特性:
Dockerfile 的每一行命令都会生成一个独立的镜像层,并且拥有唯一的 ID;
Dockerfile 的命令是完全透明的,通过查看 Dockerfile 的内容,就可以知道镜像是如何一步步构建的;
Dockerfile 是纯文本的,方便跟随代码一起存放在代码仓库并做版本管理。
1. Dockerfile 指令讲解
Dockerfile 指令 | 指令简介 |
---|---|
FROM | Dockerfile 除了注释第一行必须是 FROM ,FROM 后面跟镜像名称,代表我们要基于哪个基础镜像构建我们的容器。 |
RUN | RUN 后面跟一个具体的命令,类似于 Linux 命令行执行命令。🌹此命令在生成镜像的时候执行 |
ADD | 拷贝本机文件或者远程文件到镜像内 |
COPY | 拷贝本机文件到镜像内 |
USER | 指定容器启动的用户 |
ENTRYPOINT | 容器的启动命令 |
CMD | CMD 为 ENTRYPOINT 指令提供默认参数,也可以单独使用 CMD 指定容器启动参数。🌹此命令在启动容器的时候执行 |
ENV | 指定容器运行时的环境变量,格式为 key=value |
ARG | 定义外部变量,构建镜像时可以使用 build-arg = 的格式传递参数用于构建 |
EXPOSE | 指定容器监听的端口,格式为 [port]/tcp 或者 [port]/udp |
WORKDIR | 为 Dockerfile 中跟在其后的所有 RUN、CMD、ENTRYPOINT、COPY 和 ADD 命令设置工作目录。 |
2. Dockerfile 编写实例
示例1:一个基于python构建的容器运行环境
FROM python:3.9
ENV TZ=Asia/Shanghai
MAINTAINER Billy_Chen
WORKDIR /docker_test
VOLUME ["/container_volume1", "/container_volume2", "/container_volume3"]
ADD . .
RUN pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
EXPOSE 1110
CMD ["python", "./app.py"]
-
ENV TZ=Asia/Shanghai
-
增加时区功能。大部分的镜像都是UTC时间。大部分的容器在制作的时候,时间都是UTC时间(又是格林威治标准时间GMT),所以我们在使用的时候,容器内部时间都是和北京时间相差8小时。因此需要设置时区,以满足当前容器运行的时间为北京时间。默认时区为0,即UTC时间;东八区
-
Asia/Shanghai
是以地区命名的地区标准时,在中国叫CST。这个地区标准时会兼容历史各个时间节点。中国1986-1991年实行夏令时,夏天和冬天差1个小时,Asia/Shanghai
会兼容这个时间段。GMT-8是东八区,北京时间和东八区一致。
结论:1992年以后,在中国,
GMT-8
和Asia/Shanghai
是一样的时间,1986-1991之间,夏天会有一小时时差。
-
-
ADD . .
- 将Dockerfile所在目录下的所有文件拷贝到容器内的
/docker_test
目录下
- 将Dockerfile所在目录下的所有文件拷贝到容器内的
-
VOLUME ...
- 👌 volume的重要意义:使用了数据卷的功能,只要在本地修改,即可实现容器内同步。这样是可以实现双向绑定的,对一个目录进行操作,都会同步到另一个绑定过的目录中去。即使容器停止运行或者容器删除,仍然可以实现数据同步,本地的数据卷不会丢失。
- 通过此命令,容器创建时会自动在容器生成这三个目录。
- ⚠️ 注意:出于可分享和可移植考虑,直接在DockerFile中使用此命令,不能实现
-v 主机目录:容器目录
这种方法的效果。由于宿主机目录是依赖于特定的宿主机的,并不能够保证所有的宿主机都存在这样的特定的目录。所以还是需要docker run -v
指令去设置数据卷的绑定。
-
EXPOSE 1110
- 标示此镜像暴露了容器内的1110端口,主要是给使用这个镜像的人看的。没什么卵用,真正要映射端口还是得润的时候用-p指令。
-
CMD ["python", "./app.py"]
- 等同于在「容器启动的时候」在容器内执行cmd命令:
python ./app.py
- CMD定义的三种方式:
- CMD 这个会当作/bin/sh -c “cmd"来执行
- CMD [“executable”,“arg1”,….]
- CMD [“arg1”,“arg2”],这个时候CMD作为ENTRYPOINT的参数
- 就算Dockerfile中有CMD指令,我们仍然可以在docker run命令中带上容器启动时执行的命令,这会覆盖Dockerfile中的CMD指令指定的命令。
- 一个Dockerfile中只能有一个有效的CMD,当定义多个CMD的时候,只有最后一个才会起作用
- CMD指令案例
CMD ["/bin/bash"]
- Dockerfile中添加命令如此,即可在执行
docker run
或docker exec
的时候省略后面的/bin/bash
,只需要执行docker exec -it 容器
,即可等同于docker exec -it 容器 /bin/bash
达到的效果。
- Dockerfile中添加命令如此,即可在执行
- 等同于在「容器启动的时候」在容器内执行cmd命令:
示例2:一个基于busybox创建的hugo容器运行环境
FROM busybox:latest
MAINTAINER Billy_Chen
COPY ./hugo_0.109.0_linux-amd64 /bin/hugo
ENV TZ=Asia/Shanghai \
PATH="/bin/hugo:$PATH"
WORKDIR /blog
ENV TZ=Asia/Shanghai \ PATH="/bin/hugo:$PATH"
TZ=Asia/Shanghai
设置时区为东八区时间PATH="/bin/hugo:$PATH"
设置环境变量
3. 通过 Dockerfile 打包并运行一个项目
项目目录情况:
-
打包镜像
docker build -t demo:v1 --platform linux/amd64 .
docker build
命令用于使用 Dockerfile 创建镜像。更多指令讲解请转:https://docs.docker.com/engine/reference/commandline/build/-t demo:v1
镜像的名字及tag,通常name:tag或者name格式;可以在一次构建中为一个镜像设置多个tag--platform linux/amd64
在不同os之间打包、使用时,需要指定计算机平台。比如macOS (arm架构)构建的镜像在 Centos(AMD处理器架构)运行,需要通过此指令来指定计算机平台如:linux/amd64
linux/arm64
等。相同os、架构之间打包、使用,则不需要此指令。.
使用当前目录的Dockerfile进行编译镜像
-
创建并运行容器
docker run -itd -p 1110:1110 -e TZ=Asia/Shanghai -v /Users/billie/Desktop/Docker/docker_test/vvv:/docker_test/images demo:v1
-
-e TZ=Asia/Shanghai
将时区信息加入环境变量 -
-p 宿主机端口:容器内端口
指定映射的端口-v 宿主机目录:容器内目录
指定数据卷映射的目录
-
-
查看容器情况 -
docker inspect 容器id
发现已经挂载好目录,Source:宿主机内地址,Destination:容器内地址。双方目录发生变动时,会实时同步目录下的文件。
-
查看容器运行状态 -
docker ps
-
查看容器运行日志 -
docker logs --tail 100 容器id
4. 使用 .dockerignore 文件
在使用git时,我们可以使用.gitignore文件忽略一些不需要做版本管理的文件。同理,使用.dockerignore文件允许我们在构建时,忽略一些不需要参与构建的文件,从而提升构建效率。.dockerignore的定义类似于.gitignore。
.dockerignore的本质是文本文i件,Docker 构建时可以使用换行符来解析文件定义,每一行可以忽略一些文件或者文件夹。具体使用方式如下:
规则 | 含义 |
---|---|
# | # 开头的表示注释,# 后面所有内容将会被忽略 |
/tmp | 匹配当前目录下任何以 tmp 开头的文件或者文件夹 |
*.md | 匹配以 .md 为后缀的任意文件 |
tem? | 匹配以 tem 开头并且以任意字符结尾的文件,?代表任意一个字符 |
!README.md | ! 表示排除忽略。例如 .dockerignore 定义如下:*.md!README.md表示除了 README.md 文件外所有以 .md 结尾的文件。 |
5. 发布镜像至仓库
请移步 👉 私有镜像仓库
6. 构建多架构的镜像
默认情况下,x86_64平台只能构建x86_64镜像,如果需要在X86_64平台构建多平台镜像(比如ARM64),我们可以用Docker官方提供的Buildx工具来完成多平台镜像构建。ARM 架构与X86相比,ARM 低功耗、移动市场占比高,X86 高性能、服务器市场占比高。 构建时要用到 docker buildx 命令,Docker 版本需要 19.03+
第一步,需要初始化Docker Buildx的环境
Docker Buildx属于实验性功能,默认并没有开启,需要修改
vim /etc/docker/daemon.json
添加一行
"experimental": true
确保已经安装了 Docker 的 buildx 插件。如果没有安装,可以通过以下命令安装:
docker buildx install
-
使用方式1,由于Docker默认的builder实例不支持同时指定多个–platform,所以必须先创建一个新的builder实例,并使用。
docker buildx create --name builderx docker buildx use builderx
-
使用方式2,一步到位,使用组合命令:
docker buildx create --use
docker buildx create --use
会创建一个新的 builder,并立即将该 builder 设置为当前使用的 builder,以便在后续的构建操作中使用。这样可以简化创建和使用 builder 的过程,特别适用于临时构建的场景,无需单独执行两个命令来创建和使用 builder。
第二步,开始打包镜像
模式1,本地模式,镜像构建后默认保存在构建缓存中,没有保存在本地,所以将 type 指定为 docker;而且必须分别为不同的 CPU 架构构建不同的镜像,不能合并成一个镜像。
docker buildx build -t chenxuefan/server_script:v3/amd64 --platform linux/amd64 -o type=docker .
docker buildx build -t chenxuefan/server_script:v3/arm64 --platform linux/arm64 -o type=docker .
模式2,在线模式(推荐),通过此命令,打包成为一个多平台的镜像,并上传至线上仓库。
docker buildx build -t chenxuefan/server_script:v3 --platform linux/amd64,linux/arm64 . --push
7. 高效率构建镜像
报错解决记录
-
-bash: docker-machine: command not found
(macOS)base=https://github.com/docker/machine/releases/download/v0.14.0 && curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/usr/local/bin/docker-machine && chmod +x /usr/local/bin/docker-machine
-
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/create?fromImage=hello-world&tag=latest: dial unix /var/run/docker.sock: connect: permission denied
-
Warning: Stopping docker.service, but it can still be activated by: docker.socket
执行
systemctl stop docker
命令之后会出现如上提示,主要的意思就是dockers已经停止了,但是呢可以随时被唤醒,所以才能使用docker命令启动docker。 -
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested standard_init_linux.go:228: exec user process caused: exec format error
运行镜像报错,原因是打包的平台跟运行的平台冲突,打包时需要加上
--platform
指令 -
denied: requested access to the resource is denied
推送镜像报错,原因是需要
docker login
一下