• Docker入门之创建镜像初步


    目录

    目录 1

    1. 前言 1

    2. 基本概念 2

    2.1. 仓库 2

    2.2. 镜像ID和容器ID 2

    3. 最简镜像 2

    3.1. 目录结构 2

    3.2. hello.go 2

    3.3. Dockerfile 3

    3.4. CMD和ENTRYPOINT 3

    3.5. RUN和CMD 4

    3.6. 生成镜像 4

    3.7. 启动容器 5

    4. 镜像进阶 5

    4.1. 下载基础镜像 6

    4.2. 准备本地程序源码 6

    4.3. 编写Dockerfile 6

    4.4. 生成镜像 7

    4.5. 启动容器 7

    5. 常见问题 7

    5.1. stat /bin/sh: no such file or directory 7

    5.2. COPY failed: ... stat no such file or directory 7

    5.3. exec user process caused "no such file or directory" 8

    附:安装GO 8

    1. 前言

    本文介绍在CentOS7上从构建一个最简单无依赖的镜像开始,逐步揭示Docker镜像的构建和Dockerfile的应用。

    什么是镜像?可理解镜像(image)为一个可执行程序文件,而容器(container)则是进程(运行态),Kubernetes(即k8s)中的概念POD则相当于进程组。

    谨记:容器运行在Linux内核之上,不包含位于内核之上的glibc等库,以及ls等命令。如果容器中的程序依赖glibc等库或者依赖ls等命令,则容器自身应当包含这些设施。另外,容器中的程序等必须和内核兼容,否则将会遇到“FATAL: kernel too old”错误,该错误和库文件ld-linux.so有关。

    2. 基本概念

    2.1. 仓库

    Docker仓库(Repository)是存储Docker镜像的地方。

    2.2. 镜像ID和容器ID

    镜像(image)是静态的,容器(container)是运行中的镜像。如果说镜像是程序文件,则容器是进程。把镜像ID看作文件名,则容器ID可视为进程ID,因此每次启动的容器ID是不相同的。

    同一镜像可以启动多个容器,容器间的ID不会相同:

    # docker ps

    CONTAINER ID IMAGE     COMMAND     CREATED       STATUS       PORTS  NAMES

    7518f632b6d0 centos  "/bin/bash" 4 seconds ago Up 2 seconds        focused_turing

    d97bd379589c centos  "/bin/bash" 6 minutes ago Up 6 minutes        friendly_nightingale

    3. 最简镜像

    从最简镜像开始,有助于快速了解Dockerfile和Docker镜像的构建。

    3.1. 目录结构

    # tree /root/docker/hello

    /root/docker/hello

    |-- Dockerfile

    |-- hello

    `-- hello.go

    0 directories, 3 files

    3.2. hello.go

    GO编译出来的可执行程序不依赖libc、libdl、linux-vdso和libonion等库,可以构建最简单的Dockerfile和最小的镜像。hello.go源代码如下:

    # cat hello.go

    package main

    import "fmt"

    func main() {

      fmt.Println("Hello, world! ");

    }

    编译hello.go,生成可执行程序hello:

    # go build -o hello hello.go

    # ls

    hello  hello.go

    3.3. Dockerfile

    编写一个最简单(不基于任何已有镜像)的Dockerfile,仅将本地的hello程序打包到镜像中,并在启动容器时运行hello。内容如下:

    # cat Dockerfile

    FROM scratch

    COPY hello /

    CMD ["/hello"]

    Dockerfile格式解释:

    关键词

    说明

    #

    表示注释

    FROM

    用于指定基础镜像,scratch表示不基于任何基础镜像。

    COPY

    表示复制本地文件到容器的指定目录,注意本地文件目录是相对Dockerfile文件所在的目录,而不是系统的根目录。如果是远端的文件,则需使用ADD命令。

    CMD

    用于指定启动容器时默认执行的命令,一个Dockerfile只有最后一条CMD有效,其它的CMD会被忽略,CMD有三种书写格式。

    3.4. CMD和ENTRYPOINT

    如果在Dockerfile中没有指定ENTRYPOINT,执行命令“docker run”也没有指定“--entrypoint”,则执行CMD指定的命令。另外,可通过命令行参数“--entrypoint”覆盖ENTRYPOINT。

    Dockerfile中的CMD有三种书写格式:

     

    书写格式

    说明

    格式1

    CMD ["executable","param1","param2"]

    EXEC执行方式

    格式2

    CMD ["","param2"]

    指定了ENTRYPOINT时,作为ENTRYPOINT的参数,请注意ENTRYPOINT也分EXECShell两种书写格式。

    格式3

    CMD command param1 param2

    Shell执行方式,这要求镜像中有可执行程序“/bin/sh”,执行时实际是:

    /bin/sh -c "command param1 param2",

    如果镜像中无“/bin/sh”,则在启动容器时报错“stat /bin/sh: no such file or directory”。

    1) 什么是EXEC执行方式?

    # /bin/whoami

    root

    2) 什么是Shell执行方式?

    # sh -c "/bin/whoami"

    root

    如果CMD和ENTRYPOINT组合使用,则两者均需JSON数组格式。

    3.5. RUN和CMD

    Dockerfile中的每一条RUN命令均会产生一个新的镜像,因此应当尽可能减少RUN命令数,如使用“&&”将多条写成一条。

    RUN mkdir /data/test && chown test /data/test

    RUN和CMD完全不同,RUN是生成镜像时执行,而CMD是启动容器时执行。RUN和镜像相关,CMD和容器相关。

    3.6. 生成镜像

    执行命令“docker build”生成镜像(也叫构建镜像,一个镜像由镜像ID唯一标识),执行命令“docker images”查看镜像列表,生成镜像有点类似于编译。

    # docker images

    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

    # 参数“--tag”用于指定镜像名(或叫镜像标签),

    # 如果不指定“--tag”,则镜像名为匿名(<none>)。

    # 如果文件Dockerfile没有发生变化,

    # 则重复执行build不会生成新的镜像。

    # docker build --tag hello . # 或docker build --tag hello -f Dockerfile .

    Sending build context to Docker daemon  2.013MB

    Step 1/3 : FROM scratch

     --->

    Step 2/3 : COPY hello /

     ---> be473a78a240

    Step 3/3 : CMD /hello

     ---> Running in e6584dd16fe2

    Removing intermediate container e6584dd16fe2

     ---> 92672788bc94

    Successfully built 92672788bc94 <-- 这是镜像ID

    Successfully tagged hello:latest

    # docker images # “IMAGE ID”为镜像ID,这里值为92672788bc94

    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

    hello               latest              92672788bc94        2 seconds ago       2.01MB

    3.7. 启动容器

    最简单的启动容器方法:

    # docker run hello

    Hello, world!

    也可如下方式启动容器:

    docker run -it hello

    docker run -i -t hello

    也可带上“--rm”参数(容器停止后自动删除):

    docker run -it --rm hello

    这里的参数“-i”和参数“-t”,分别表示:

    参数

    作用

    -i

    i是interactive的缩写,作用是让容器的标准输入保持打开以进入命令交互界面模式

    -t

    t是tty的缩写,作用是让docker分配一个伪终端并绑定到容器的标准输入上

    -d

    d是deamon的缩写,作用是让容器以后台守护方式运行

    -p

    p是port的缩写,作用是指定端口映射

    -P

    P是port的缩写,作用是随机分配端口

    --name

    为容器指定一个新的名字

    --rm

    容器退出时自动删除,如果不指定,则需要通过命令“docker rm”来删除

    4. 镜像进阶

    这一节的镜像不从零开始,而是基于已有镜像生成新的镜像。

    从scratch创建一个实用的镜像不易,也是不必要的,除了学习目的。容器虽然运行在本地的Linux内核之上,但依赖的库(运行时环境)却需要容器本身包含,比如核心的libc和libdl等库。这也是在创建最简镜像时采用GO程序的原因,避免了这些依赖,然而实际中很难避免这些依赖,因此最好的办法是基于其它镜像构建自己的镜像。

    alpine是Docker官方提交的只有5MB多大小的Linux镜像,包管理工具为apk,可以用来做学习研究用。alpine不带glibc库,它带的是musl libc(一个轻量级的C标准库)。如果有glibc需求,可用基于alpine的alpine-glibc镜像,这个也有Docker官方提供的。

    另外,还有一个第三方的tinycore镜像,只有7MB多大小,包含了libc等更为丰富基础设施。如果可以访问docker.io,则可直接执行命令“docker pull tinycore”将tinycore镜像拉取到本地,否则通过Docker的镜像导出(先在一台可以访问docker.io机器上pull镜像,然后导出成tar文件)和导入功能间接拉取到。

    不同的基础镜像除了所带的库等不同外,镜像大小也是考虑的重要因素之一,原则上越小越好,本节内容官方的Centos镜像。

    4.1. 下载基础镜像

    这里选择官方的centos作为基础镜像,执行拉取镜像命令:

    # docker pull docker.io/centos

    如想找其它的centos镜像,可执行命令“docker search centos”搜索。如果本地不能访问docker.io,则可在一台可访问docker.io机器先拉取下来,然后使用Docker的导出(save)导入(load)载入进来。

    检查centos镜像是否可用:

    # docker images | grep centos

    centos              latest              0f3e07c0138f        2 months ago        220MB

    检查镜像centos版本:

    # docker run -it --rm centos cat /etc/centos-release

    CentOS Linux release 8.0.1905 (Core)

    4.2. 准备本地程序源码

    以C程序为例,源代码如下:

    # cat echo1.c

    #include <stdio.h>

    int main(int argc, char* argv[]) {

      if (argc == 1) printf("=> ECHO1: docker ");

      else printf("=> ECHO1: %s ", argv[1]);

    }

    编译生成可执行程序:

    # gcc -g -o echo1 echo1.c

    4.3. 编写Dockerfile

    # cat Dockerfile.echo1

    FROM centos

    COPY echo1 /

    CMD ["/echo1"]

    4.4. 生成镜像

    # docker build --tag echo1 -f Dockerfile.echo1 .

    4.5. 启动容器

    默认不带参数方式运行(因为Dockerfile.echo1中没有ENTRYPOINT,所以执行的是CMD部分命令):

    # docker run -it --rm echo1

    => Hello: docker

    带参数方式执行(实为“--entrypoint”方式):

    # docker run -it --rm echo1 /echo1 centos

    => ECHO1: centos

    上述等同于:

    # docker run -it --rm --entrypoint='/echo1' echo1

    => ECHO1: docker

    “--entrypoint”带参数方式如下(参数在最后,并不是“--entrypoint”值的一部分):

    # docker run -it --rm --entrypoint='/echo1' echo1 world

    => ECHO1: world

    5. 常见问题

    5.1. stat /bin/sh: no such file or directory

    启动窗口时报如下错误,可能是Dockerfile中的CMD格式错误:

    docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: "/bin/sh": stat /bin/sh: no such file or directory": unknown.

    ERRO[0000] error waiting for container: context canceled

    原因是CMD书写为Shell格式,但镜像中没有/bin/sh这个文件。

    5.2. COPY failed: ... stat no such file or directory

    在创建镜像时报如下错误,是因为COPY命令的源文件或目录不是相对Dockerfile所在目录的路径,比如使用了本地路径。

    COPY failed: stat /data/docker/tmp/docker-builder891858880/bin/sh: no such file or directory

    比如下列COPY即会报这个错误:

    COPY /bin/sh /bin/

    解决办法是先将/bin/sh复制到Dockerfile文件所在目录,然后再创建镜像。

    5.3. exec user process caused "no such file or directory"

    运行容器时报如下错误:

    standard_init_linux.go:211: exec user process caused "no such file or directory"

    这个错误有多种原因,比如:

    1) Dockerfile非UNIX格式(换符符);

    2) 容器中的可执行程序依赖的库不存在,比如没有libc库;

    3) CMD格式错误。

    附:安装GO

    安装GO步骤:

    1) 下载安装包

    从GO的官网(https://golang.org/dl/)上下载,选择Linux安装包(本文下载的为go1.13.5.linux-amd64.tar.gz)。

    2) 上传安装包

    将安装包(比如go1.13.5.linux-amd64.tar.gz)上传到/usr/local目录。如果Linux能够访问网络,也可直接在/usr/local上下载,比如:

    # cd /usr/local

    # wget https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz

    3) 安装和设置

    在/usr/local目录下解压即完成安装,实际上也可能解压到其它目录。

    # cd /usr/local

    # tar xzf go1.13.5.linux-amd64.tar.gz

    设置环境变量,以方便执行(go.sh可无可执行权限):

    # cat /etc/profile.d/go.sh

    export PATH=/usr/local/go/bin:$PATH

    如果不想重新登录而直接生效,可手工直接执行一次go.sh:

    # source /etc/profile.d/go.sh

  • 相关阅读:
    解决无法进入安全模式
    解决SQLite找不到数据表的问题
    利用js的垃圾回收原理来理解闭包(Closure)问题
    《降级论》《按时交作业的学生何以常穿脏袜子》读后感
    ie6下出现“缺少标识符、字符串或者数字”
    两个想法
    WdatePicker.js帮助文档
    最近一直在研究网站的推广。。。
    这个。。。。我不知道说什么。。。
    今天忽然发现,最近做的网站的一个目标关键字已经实现了。。。
  • 原文地址:https://www.cnblogs.com/aquester/p/12086388.html
Copyright © 2020-2023  润新知