Docker: hello world
文章目录
- 一、构建第一个镜像
- 二、问题与解决方法
一般安装完Docker之后,都会拉取官方的hello-world镜像运行下看看Docker是否正确安装、能否正常运行(docker run hello-world
),以下是该镜像的相关链接:
-
docker hello-world镜像地址:https://hub.docker.com/_/hello-world
-
相关的源码:https://github.com/docker-library/hello-world
所以这个镜像是怎么制作出来的,今天我们自己也来试试看,构建自己的第一个镜像my-hello-world
~
一、构建第一个镜像
在这过程中,我把遇到的问题及其解决方法都汇总在第二部分,所以如果在构建镜像的过程中有遇到问题,可以先到第二部分看看有没有对应的问题和解决方法
-
创建一个目录存放镜像相关文件/turn
mkdir my-hello-world cd my-hello-world
-
创建镜像所需的可执行文件
i. 先新建文件
hello.c
:vim hello.c
ii. 将下面的代码复制到上面的文件中,这里直接使用官方github上的hello.c的源代码:
#include <sys/syscall.h> #include <unistd.h> #ifndef DOCKER_IMAGE #define DOCKER_IMAGE "hello-world" #endif #ifndef DOCKER_GREETING #define DOCKER_GREETING "Hello from Docker!" #endif #ifndef DOCKER_ARCH #define DOCKER_ARCH "amd64" #endif const char message[] = "\n" DOCKER_GREETING "\n" "This message shows that your installation appears to be working correctly.\n" "\n" "To generate this message, Docker took the following steps:\n" " 1. The Docker client contacted the Docker daemon.\n" " 2. The Docker daemon pulled the \"" DOCKER_IMAGE "\" image from the Docker Hub.\n" " (" DOCKER_ARCH ")\n" " 3. The Docker daemon created a new container from that image which runs the\n" " executable that produces the output you are currently reading.\n" " 4. The Docker daemon streamed that output to the Docker client, which sent it\n" " to your terminal.\n" "\n" "To try something more ambitious, you can run an Ubuntu container with:\n" " $ docker run -it ubuntu bash\n" "\n" "Share images, automate workflows, and more with a free Docker ID:\n" " https://hub.docker.com/\n" "\n" "For more examples and ideas, visit:\n" " https://docs.docker.com/get-started/\n" "\n"; int main() { //write(1, message, sizeof(message) - 1); syscall(SYS_write, STDOUT_FILENO, message, sizeof(message) - 1); //_exit(0); //syscall(SYS_exit, 0); return 0; }
iii. 编译源代码生成可执行文件:
gcc -static hello.c -o hello
如果提示
gcc
命令没找到,直接按照提示安装该命令iv. 运行生成的
hello
可执行文件,看能否正常输出看起来程序运行和输出都没问题
-
编写Dockerfile
Dockerfile是用于构建镜像的文本文件,里面包含了构建镜像所需的指令,每一条指令构建一层镜像,指令的内容描述了该层镜像应如何构建。这里也直接使用官方github上提供的Dockerfile
新建文件Dockerfile,并将下面的代码复制进去:
FROM scratch COPY hello / CMD ["/hello"]
简单解释下上述构建镜像的指令:
-
FROM scratch
:以scratch
镜像为基础,从scratch
这个镜像开始构建我们的镜像,scratch
镜像的介绍可以看这里。 -
COPY hello /
:将可执行文件hello
复制到镜像文件系统的根目录 -
CMD ["/hello"]
:执行命令/hello
-
-
创建镜像
强调一下,到目前为止,所有的文件都是在同一目录(这里是my-hello-world)下。
先确保是在my-hello-world这个目录下面,然后执行如下命令开始构建镜像:
sudo docker build -t my-hello-world:latest .
已经成功构建了
my-hello-world
镜像,执行sudo docker images
命令也可以看到已经有了我们刚刚构建好的镜像 -
运行镜像
sudo docker run my-hello-world
可以看到自己制作的镜像可以正常运行输出了,跟官方提供的一样。
二、问题与解决方法
-
编译
hello.c
时,出现错误/usr/bin/ld: cannot find -lc collect2: error: ld returned 1 exit status
这是因为在安装
gcc
的过程中,默认只安装动态库libc.so
,没有安装静态库libc.a
,所以需要手动安装,根据这里的说明,执行以下命令安装相应的静态库:sudo dnf --enablerepo=crb install glibc-static
这时再执行编译命令
gcc -static hello.c -o hello
就没问题了。 -
如果编译时不加
-static
选项,则输出的可执行文件在Linux上可以正常运行,而在运行镜像时会产生问题exec /hello: no such file or directory
这是因为如果直接使用
gcc
编译,不加任何选项,则默认时使用动态链接的方式编译,用到的动态库不会被编译进目标程序中,输出的可执行文件无法单独运行,需要系统提供相应的动态库才能正常运行。因为Linux中提供了动态库,所以hello
在Linux中可以运行,而在镜像中,我们没有安装gcc
,自然不会有运行程序需要的动态库,所以从这个镜像启动的容器自然也无法运行hello
程序。加上-static
选项可以让gcc
使用静态编译的方式编译源代码,会把静态库添加编译到目标程序中,编译输出的可执行文件可独立运行,不需要系统提供其他的库文件,所以这个hello
程序在容器中也可以正常运行。因为静态编译会把静态库编译进目标程序,所以静态编译输出的程序会比动态编译输出的程序要大很多:
可以使用
file
命令查看可执行文件是使用何种编译方式编译的