创建、运行及共享容器镜像(把应用打包成镜像,推送到镜像库)

详细内容请见书:《Kubernetes in Action》2.1节: 创建、运行及共享容器镜像
 
 

1 运行容器镜像原理

 
        Docker hub 中有许多随时可用的常见镜像,其中就包括busybox ,可以用来运行简单的echo "Hello world"命令。
        busybox 是一个单一可执行文件,包含多种标准UN IX 命令行工具,如: echo 、ls 、gzip 等
 
        使用docker run 命令然后指定需要运行的镜像的名字
[root@centos7-01 ~]# docker run busybox echo "Hello world"
Unable to find image 'busybox:latest' locally
Trying to pull repository docker.io/library/busybox ...
latest: Pulling from docker.io/library/busybox
61c5ed1cbdf8: Pull complete
Digest: sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977
Status: Downloaded newer image for docker.io/busybox:latest
Hello world

        图2.1 展示了执行docker run 命令之后发生的事情。首先, Docker 会检查busybox:latest 镜像是否己经存在于本机。如果没有,Docker 会从http: //docker.io 的Docker 镜像中心拉取镜像。镜像下载到本机之后, Docker 基于这个镜像创建一个容器并在容器中运行命令。ec h o 命令打印文字到标准输出流,然后进程终止,容器停止运行。

 
#运行其他的容器镜像命令:

$ docker run <image>

$ docker run <image>:<tag>

#tag为镜像的版本,当引用镜像没有显式地指定tag 时, Docker 会默认指定tag 为latest

 

2 构建一个简单的Node.js Web 应用

 
        接下来会构建一个简单的Node.js Web 应用,并把它打包到容器镜像中。这个应用会接收HTTP 请求并响应应用运行的主机名。这样,应用运行在容器中,看到的是自己的主机名而不是宿主机名,即使它也像其他进程一样运行在宿主机上。 这在后面会非常有用,当应用部署 在Kubernetes 上并进行伸缩时(水平伸缩,复制应用到多个节点),你会发现HTTP 请求切换到了应用的不同实例上。
 
#代码清单2.2 一个简单的Node.js应用: app.js
[root@centos7-01 docker_test]# cat app.js
const http = require('http');
const os = require('os');


console.log("Kubia server starting ...");


var handler = function(request, response) {
  console.log("Received request from " + request.connection.remoteAddress);
  response.writeHead(200);
  response.end ("You've hit " + os.hostname() + "\n");
};
var www = http.createServer(handler);
www.listen(8080);

        代码实现的功能:在8080 端口启动了一个HTTP 服务器。服务器会以状态码200 OK 和文字"You've hit <hostname>" 来响应每个请求。请求handler 会把客户端的IP 打印到标准输出,以便日后查看。

注意:返回的主机名是服务器真实的主机名,不是客户端发出的HTTP 请求中头 的Host 字段。
 
 

为镜像创建Dockerfile

 
       为了把应用打包成镜像,首先需要创建一个叫Dockerfile 的文件,它包含了一 系列构建镜像日才会执行的指令。Dockerfile 文件需要和app.j s 文件在同一目录,并包 含下面代码清单中的命令。
 
#代码:青单2.3 构建应用容器镜像的Dockerfile
[root@centos7-01 docker_test]# cat Dockerfile
FROM node:7
ADD app.js /app.js
ENTRYPOINT ["node", "app.js"]

 From 行定义了镜像的起始内容(构建所基于的基础镜像) 。这个例子中使用的是node 镜像的tag 7 版本。

第二行中把app.js 文件从本地文件夹添加到镜像的根目 录,保持app.js这个文件名。
最后一行定义了当镜像被运行时需要被执行的命令, 这个例子中,命令是node app.j s 。
 
 
选择基础镜像:
       你或许在想,为什么要选择这个镜像作为基础镜像。因为这个应用是Node.js 应用,镜像需要包含可执行的node 二进制文件来运行应用。你也可以使用任何包含这个二进制文件的镜像,或者甚至可以使用Linux 发行版的基础镜像,如fedora 或ubuntu ,然后在镜像构建的时候安装Node.js 。但是由于node镜像是专门用来运行No de 扣应用的,并且包含了运行应用所需的一切, 所以把 它当作基础镜像。
 
 

4 构建容器镜像

 
现在有了Dockerfile 和app.js 文件,这是用来构建镜像的所有文件。运行下面 的Docker命令来构建镜像:
$ docker build -t kubia

        图2.2 展示了镜像构建的过程。用户告诉Docker 需要基于当前目录(注意命令结尾的点)构建一个叫kubi a 的镜像, Docker 会在目录中寻找Dockerfile ,然后基于其中的指令构建镜像。

 
 
 
镜像是如何构建的
        构建过程不是由Docker客户端进行的, 而是将整个目录的文件上传到Docker守护进程并在那里进行的。Docker客户端和守护进程不要求在同一台机器上。如果你在一台非Linux操作系统中使用Docker, 客户端就运行在你的宿主操作系统上,但是守护进程运行在一个虚拟机内。由千构建目录中的文件都被上传到了守护进程中, 如果包含了大量的大文件而且守护进程不在本地运行, 上传过程会花费更多的时间。
 
        提示:不要在构建目录中包含任何不需要的文件, 这样会减慢构建的速度——尤其当Docker守护进程运行在一个远瑞机器的时候。
        在构建过程中, Docker首次会从公开的镜像仓库(Docker Hub)拉取基础镜像(node:7), 除非已经拉取过镜像并存储在本机上了。
 
 
镜像分层
       镜像不是一个大的二进制块, 而是由多层组成的, 在运行busybox例子时你可能已经注意到(每一层有一行Pull complete), 不同镜像可能会共享分层, 这会让存储和传输变得更加高效。比如, 如果创建了多个基于相同基础镜像(比如例子中的node:7)的镜像,所有组成基础镜像的分层只会被存储一次。拉取镜像的时候,Docker 会独立下载每一层。一些分层可能已经存储在机器上了,所以Docker只会下载未被存储的分层。
       你或许会认为每个Dockerfile 只创建一个新层,但是并不是这样的。构建镜像时,Dockerfile 中每一条单独的指令都会创建一个新层。镜像构建的过程中,拉取基础镜像所有分层之后,Docker 在它们上面创建一个新层并且添加app.js。然后会创建另
一层来指定镜像被运行时所执行的命令。最后一层会被标记为kub迳:latest。图2.3 展示了这个过程,同时也展示另外一个叫other:la七est 的镜像如何与我们构建的镜像共享同一层Node.js 镜像。
 
 
构建完成时,新的镜像会存储在本地。下面的代码展示了如何通过Docker 列出本地存储的镜像:
[root@centos7-01 docker_test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
kubia               latest              803af35b8aec        10 minutes ago      660 MB
docker.io/busybox   latest              018c9d7b792b        6 days ago          1.22 MB
docker.io/node      7                   d9aed20b68a4        2 years ago         660 MB

 

5 运行容器镜像

 
以下的命令可以用来运行镜像:
$ docker run --name kubia-container -p 8080:8080 -d kubia

       这条命令告知Docker基于kubia镜像创建一个叫kubia-container的新容器。这个容器与命令行分离(- d标志),这意味着在后台运行。本机上的8080端口会被映射到容器内的8080 端口(-p 8080:8080选项),所以可以通过http://localhost:8080访问这个应用。

[root@centos7-01 docker_test]# curl localhost:8080
You've hit 60dfafaec47d

#或者在浏览器上:输入地址:把192.168.159.128替换成你的虚拟机的ip
http://192.168.159.128:8080/

常用命令列表:

#列出运行中的容器
$ docker ps

#docker ps只会展示容器的大部分基础信息。 可以使用docker inspect查看更多的信息:
$ docker inspect kubia-container

#Docker 会打印出包含容器底层信息的长JSON。

 

探索运行容器的内部

 
       我们来看看容器内部的环境。由于一个容器里可以运行多个进程,所以总是可以运行新的进程去看看里面发生了什么。如果镜像里有可用的shell 二进制可执行文件,也可以运行一个shell。
       在已有的容器内部运行shell镜像基于的Node.js 镜像包含了bash shell, 所以可以像这样在容器内运行shell:
 
$ docker exec -it kubia-container bash

       这会在已有的kubia-container容器内部运行bash。bash 进程会和主容器进程拥有相同的命名空间。这样可以从内部探索容器,查看Node.js 和应用是如何在容器里运行的。-it 选项是下面两个选项的简写:

• -i,确保标准输入流保待开放。需要在shell 中输入命令。
• -t, 分配一个伪终端(TTY)。
       如果希望像平常一样使用shell, 需要同时使用这两个选项(如果缺少第一个选项就无法输入任何命令。如果缺少第二个选项,那么命令提示符不会显示,并且一些命令会提示TERM变量没有设置)。
 
       从内部探索容器
      下面的代码展示了如何使用shell 查看容器内运行的进程。
#从容器内列出进程
roo七@44d76963e8el:/# ps aux

#运行在主机操作系统上的容器进程
$ ps aux | grep app.js

       这证明了运行在容器中的进程是运行在主机操作系统上的。如果你足够敏锐,会发现进程的ID在容器中与主机上不同。容器使用独立的PIDLinux命名空间并且有着独立的系列号,完全独立于进程树。

       
        容器的文件系统也是独立的
       正如拥有独立的进程树一样,每个容器也拥有独立的文件系统。在容器内列出根目录的内容,只会展示容器内的文件,包括镜像内的所有文件,再加上容器运行时创建的任何文件(类似日志文件),如下面的代码清单所示。
 
 
其他常用命令:
#通过告知Docker停止kubia-container容器来停止应用
$ docker stop kubia-container

因为没有其他的进程在容器内运行,这会停止容器内运行的主进程。容器本身仍然存在并且可以通过docker ps -a来查看。-a选项打印出所有的容器,包括运行中的和已经停止的。想要真正地删除一个容器,需要运行docker rm:

$ docker rm kubia-container

 

 

向镜像仓库推送镜像

 
       现在构建的镜像只可以在本机使用。为了在任何机器上都可以使用, 可以把镜像推送到一个外部的镜像仓库。为了简单起见, 不需要搭建一个私有的镜像仓库,而是可以推送镜像到公开可用的Docker Hub(http://hub.docker.com) 镜像中心。另外还有其他广泛使用的镜像中心, 如Quay.io 和Google Container Registry 。
       在推送之前, 需要重新根据Docker Hub 的规则标注镜像。Docker Hub 允许向以你的Docker Hub ID 开头的镜像仓库推送镜像。可以在http://hub.docker.com 上注册Docker Hub ID。下面的例子中会使用笔者自己的ID (luksa), 请在每次出现时替换自己的ID。
 
       使用附加标签标注镜像
       一旦知道了自己的ID, 就可以重命名镜像, 现在镜像由kubia 改为luksa/kubia(用自己的Docker Hub ID 代替luksa):
 
$ docker tag kubia luksa/kubia
 
      这不会重命名标签, 而是给同一个镜像创建一个额外的标签。可以通过docker images 命令列出本机存储的镜像来加以确认, 如下面的代码清单所示。
 
 
      向Docker Hub 推送镜像
在向Docker Hub 推送镜像之前, 先需要使用docker login 命令和自己的用户ID 登录, 然后就可以像这样向Docker Hub 推送yourid/kubia 镜像:
 
$ docker push luksa/kubia
      在不同机器上运行镜像
     在推送完成之后, 镜像便可以给任何人使用。可以在任何机器上运行下面的命令来运行镜像:
 
$ docker run -p 8080:8080 -d luksa/kubia
     这非常简单。最棒的是应用每次都运行在完全一致的环境中。如果在你的机器上正常运行, 也会在所有的Linux 机器上正常运行。无须担心主机是否安装了Node.js。事实上,就算安装了,应用也并不会使用,因为它使用的是镜像内部安装的。
 
 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值