「docker」学习笔记 | 镜像、容器、仓库

关于docker

1. why docker?

1_JUOITpaBdlrMP9D__-K5Fw

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 不得不在战争中改变一些技术架构。最终形成了下图所示的技术架构。

Ciqc1F9PYtCAC1GSAADIK4E6wrc368/Docker 架构图/

我们可以看到,Docker 整体架构采用 C/S(客户端 / 服务器)模式,主要由客户端和服务端两大部分组成。客户端负责发送操作指令,服务端负责接收和处理指令。客户端和服务端通信有多种方式,既可以在同一台机器上通过UNIX套接字通信,也可以通过网络连接远程通信。

  1. Docker Client - 客户端

    Docker 客户端其实是一种泛称。其中 docker 命令是 Docker 用户与 Docker 服务端交互的主要方式。除了使用 docker 命令的方式,还可以使用直接请求 REST API 的方式与 Docker 服务端交互,甚至还可以使用各种语言的 SDK 与 Docker 服务端交互。目前社区维护着 Go、Java、Python、PHP 等数十种语言的 SDK,足以满足你的日常需求。

  2. Docker Daemon - 守护进程,即服务器端

    Docker 服务端是 Docker 所有后台服务的统称。其中 dockerd 是一个非常重要的后台管理进程,它负责响应和处理来自 Docker 客户端的请求,然后将客户端的请求转化为 Docker 的具体操作。例如镜像、容器、网络和挂载卷等具体对象的操作和管理。

    Docker 从诞生到现在,服务端经历了多次架构重构。起初,服务端的组件是全部集成在 docker 二进制里。但是从 1.11 版本开始, dockerd 已经成了独立的二进制,此时的容器也不是直接由 dockerd 来启动了,而是集成了 containerd、runC 等多个组件。

    虽然 Docker 的架构在不停重构,但是各个模块的基本功能和定位并没有变化。它和一般的 C/S 架构系统一样,Docker 服务端模块负责和 Docker 客户端交互,并管理 Docker 的容器、镜像、网络等资源。

3. 四大核心概念

  1. Docker Images(镜像):Docker Images是一个只读模板,它包含创建 Docker容器的说明。它和系统安装光盘有点像(或者面向对象中的类),使用系统安装光盘可以安装系统,同理,使用 Docker镜像可以运行 Docker镜像中的程序
  2. Docker Container(容器):Docker Container是镜像的可运行实例(面向对象中的实例化对象)镜像和容器的关系有点类似于面向对象中,类和对象的关系。可通过 Docker API或者CL命令来启停、移动、删除容器
  3. Docker Repository(仓库):Docker 的镜像仓库类似于代码仓库,用来存储和分发 Docker 镜像。镜像仓库分为公共镜像仓库和私有镜像仓库。镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,这时候就用到了仓库。目前,Docker Hub 是 Docker 官方的公开镜像仓库,它不仅有很多应用或者操作系统的官方镜像,还有很多组织或者个人开发的镜像供我们免费存放、下载、研究和使用。除了公开镜像仓库,你也可以构建自己的私有镜像仓库
  4. Docker Registry(注册服务器):Docker Registry是一个「集中存储」与「分发镜像」的服务,相当于一个仓库。注册服务器是存放仓库的实际服务器,而仓库则可以被理解为一个具体的项目或者目录;注册服务器可以包含很多个仓库,每个仓库又可以包含多个镜像。Docker Registry可分为公有 Docker Registry和私有Docker Registry。最常用的 Docker Registry是官网的Docker Hub,这也是默认的 Docker Registry。Docker Hub上存放若大量优秀的镜像,可使用 Docker命令下载并使用

image-20200829225002743/关系图/

3731619947187_.pic/镜像、容器、仓库/

注册服务器/registry、repository、image/

4. 开发人员和生产环境为什么要使用 Docker?

作为一种新兴的虚拟化方式,Docker跟传统的虚拟化方式相比具有众多的优势。

  1. Docker容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多
  2. Docker对系统资源的利用率很高,一台主机上可同时运行数千个 Docker容器
  3. 容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销小,比虚拟机节省内存,启动更快
  4. 传统虚拟机方式运行10个不同的应用就要起10个虚拟机,而 Docker只需要启动10个隔离的应用即可,快速部署,安全运行,不污染系统

1*XBDzH8kPBzO5oW492NdLDg/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.0
  • sudo systemctl start docker - 启动docker
  • sudo systemctl enable docker - 设置开机自启动
  • docker version - 查看版本
  • 安装docker-compose - https://www.jianshu.com/p/5ba9f9159696

2. macOS

  1. 下载离线安装包

  2. 安装完输入命令检查是否成功:

    docker --version
    docker-compose --version
    docker-machine --version
    
  3. 设置docker自启动

    systemctl enable docker
    
  4. docker的启动、停止、重启

    service docker start/stop/restart
    或
    systemctl start/stop/restart docker
    
  5. 查看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 ubuntudocker 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 下载这个镜像),以下为运行信息:

image-20200831115033562

阅读运行信息,可以得知执行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]]

image-20200915015337406/关系图/

详细参数解析

-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)创建并启动容器

两种方式:

  1. docker create ... + docker start ...

    创建容器,启动容器。

    例: 创建Ubuntu容器:docker create -it --name=ubuntu16 ubuntu:16.04

    • 创建之前需确认使用的Ubuntu镜像版本,使用命令docker images -adocker ps -a

      image-20201028135035471

    • -i - 打开STDIN

    • -t - 分配一个伪终端,同时使用 -it 参数可以让我们进入交互模式

    • ubuntu:16.04 - 这是使用到的镜像,需指定版本号,如不指定版本号,则默认选取latest版本的镜像

    • --name=ubuntu16 - 为创建的容器命名为ubuntu16,注意是两个--

  2. 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 和 不指定
        1. 不指定(默认)。即不设置-v指令,容器或宿主机文件修改,双方都互不影响
        2. rw (read-write)
          • 文件:不管是宿主机还是容器内修改,都会相互同步;但容器内不允许删除,会提示Device or resource busy;宿主机删除文件,容器内的不会被同步
          • 文件夹:不管是宿主机还是容器内修改、新增、删除文件,都会相互同步
        3. 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容器的IDNAME,可以使用docker ps -adocker 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端口进行映射。

    1. 映射一个端口 docker run -itd -p 8080:80
    2. 映射多个端口 docker run -itd -p 8088:80 -p 8088:80 nginx
    3. 映射到指定端口的指定地址 docker run -itd -p 39.108.109.60:8080:80 nginx
    4. 映射到指定端口的任意地址 docker run -itd -p 39.108.109.60::80 nginx
    5. 查看端口的映射情况 docker port 容器名
  • 容器已创建时,如若需要给予容器以更多的端口映射,则可以通过以下三种方式实现

    1. 配置防火墙转发端口

    2. (靠谱)把容器打包成镜像,然后以该镜像为基础新建一个容器

    3. 修改容器配置文件的方式进行修改

      • (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 数值。

          image-20210503143157721

          或通过docker inspect name/ID|grep Id搜索查看:

          image-20210522173154555

        • 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. 私有镜像仓库

  1. 注册Docker Hub账号 - Docker Hub

  2. 创建自己的仓库 - https://hub.docker.com/repository/create

  3. 本地登录镜像服务器 - docker login

    docker login命令默认会请求 Docker Hub,如果你想登录第三方镜像仓库或者自建的镜像仓库,在docker login后面加上注册服务器即可。

  4. 重命名镜像 - docker tag

    docker tag blog:v1 用户名/blog:v1
    
  5. 本地推送镜像至仓库 - docker push

    docker push 用户名/blog:v1
    
  6. over

    image-20221020165408338

使用 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-8Asia/Shanghai是一样的时间,1986-1991之间,夏天会有一小时时差。

  • ADD . .

    • 将Dockerfile所在目录下的所有文件拷贝到容器内的/docker_test目录下
  • 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 rundocker exec的时候省略后面的/bin/bash,只需要执行docker exec -it 容器,即可等同于docker exec -it 容器 /bin/bash 达到的效果。

示例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 打包并运行一个项目

项目目录情况:image-20221019155804878

  • 打包镜像

    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

    image-20221019170058652

    发现已经挂载好目录,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。

image-20230208164852894

第二步,开始打包镜像

模式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

    https://blog.csdn.net/u011337602/article/details/104541261

  • 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一下

后记


10805 字