通过前文,我们已经知道了Docker是一个开源的应用容器引擎,并且也学会了安装Docker的几种方法及基础命令。接下来,此文将进一步继续学习Docker知识——Docker容器的相关概念。
1.前言
在学习Docker之前,如果您要编写一个Python应用程序,那么第一件事就是在您的机器上搭建Python开发环境。但是,使用Docker之后,您只需要获取一个可移植的Python开发环境镜像就可以了,然后在此基础上开发您的应用程序,最后还可以将其构建成一个新的镜像。这些可移植的镜像由Dockerfile定义。
2.Dockerfile
Dockerfile定义了容器内部的环境。容器内部环境与操作系统的其他部分是隔离的,并且在容器中,对网络接口和磁盘驱动器等资源的访问都是虚拟化的。因此,您需要将此环境中的端口映射到外部世界,同时指定您要复制到该环境中的文件。在此之后,您就可以将在此Dockerfile中定义的应用程序分发到其他地方,并保证其行为完全相同。
(1)创建Dockerfile
创建一个空目录并进入该目录,在该目录下创建一个名为Dockerfile的文件,然后将以下内容复制粘贴到Dockerfile中并保存。此时,一个简单的Dockerfile文件就创建好了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[leo@ubuntu part02]$ cat Dockerfile # use an official Python runtime as a parent image FROM python:2.7-slim # Set the working directory to /app WORKDIR /app # Copy the current directory contents into the container at /app ADD . /app # Install any needed packages specified in requirements.txt RUN pip install --trusted-host pypi.python.org -r requirements.txt # Make port 80 available to the world outside this container EXPOSE 80 # Define environment variable ENV NAME World # Run app.py when the container launches CMD ["python", "app.py"] |
其中,该Dockerfile涉及的文件app.py和requirements.txt稍后介绍。
(2)The app itself
接下来,在Dockerfile同目录下创建requirements.txt和app.py文件,当我们通过Dockerfile构建镜像时,上述Dockerfile中所示的ADD命令就会将当前目录下的requiremets.txt, app.py文件添加到镜像中的/app目录下。并且,EXPOSE命令使得app.py的输出得以通过HTTP访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[leo@ubuntu part02]$ cat requirements.txt Flask Redis [leo@ubuntu part02]$ cat app.py from flask import Flask from redis import Redis, RedisError import os import socket # Connect to Redis redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2) app = Flask(__name__) @app.route("/") def hello(): try: visits = redis.incr("counter") except RedisError: visits = "<i>cannot connect to Redis, counter disabled</i>" html = "<h3>Hello {name}!</h3>" \ "<b>Hostname:</b> {hostname}<br/>" \ "<b>Visits:</b> {visits}" return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits) if __name__ == "__main__": app.run(host='0.0.0.0', port=80) |
结合requirements.txt和Dockerfile,我们知道此Python应用程序所需的Flask库和Redis库会通过pip命令进行安装,并且该应用程序会打印输出环境变量NAME。但是,由于我们在此环境中并没有安装Redis服务,所以,如应用程序中的代码所示,会输出相应的错误信息。
(3)Build the app
目前,我们已经创建了三个文件:
1 2 3 4 |
[leo@ubuntu part02]$ ll -rw-rw-r-- 1 leo leo 666 Sep 5 01:39 app.py -rw-rw-r-- 1 leo leo 513 Sep 5 01:26 Dockerfile -rw-rw-r-- 1 leo leo 12 Sep 5 01:37 requirements.txt |
此时,就可以构建我们的Docker镜像了:
1 2 3 4 5 6 7 8 9 10 |
[leo@ubuntu part02]$ sudo docker build -t friendlyhello . [sudo] password for leo: Sending build context to Docker daemon 5.12kB Step 1/7 : FROM python:2.7-slim 2.7-slim: Pulling from library/python 802b00ed6f79: Pull complete 10b2d5f7ed73: Pull complete 1073a127cf89: Pull complete 90283f3dc1cd: Pull complete ... |
其中,-t选项指定生成镜像的名称。
最后,查看镜像:
1 2 3 4 5 |
[leo@ubuntu part02]$ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE friendlyhello latest bc9f9ece52cf About a minute ago 132MB python 2.7-slim c9cde4658340 4 hours ago 120MB hello-world latest 2cb0d9787c4d 8 weeks ago 1.85kB |
(4)Run the app
使用run命令运行应用程序:
[leo@ubuntu part02]$ sudo docker run -p 40000:80 friendlyhello
其中,-p选项表示将机器的40000端口映射到容器发布的80端口上,所以你应该在web浏览器通过链接localhost:40000而不是localhost:80访问:
图2-1
同样可以通过命令行的方式访问:
1 2 |
[leo@ubuntu ~]$ curl http://localhost:40000 <h3>Hello World!</h3><b>Hostname:</b> 321ff0370155<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i> |
如果您需要后台运行这个应用程序的话,使用-d选项即可:
1 |
[leo@ubuntu part02]$ sudo docker run -d -p 40000:80 friendlyhello |
查看正在运行的容器:
1 2 3 |
[leo@ubuntu part02]$ sudo docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bfd481ed26c7 friendlyhello "python app.py" 56 seconds ago Up 34 seconds 0.0.0.0:40000->80/tcp peaceful_colden |
停止容器,使用命令:
1 |
[leo@ubuntu part02]$ sudo docker container stop bfd48 |
其中,bfd48(不需要写全,id的前几个字符即可)表示此容器的id。
3.上传镜像
将我们刚刚创建的镜像上传之后,我们就可以在其他地方使用它了,即Docker的可移植性得以保证。注册表(registery)是存储库(repositories)的集合,而存储库又是镜像(images)的集合——类似于GitHub存储库。注册表上的账户可以创建许多存储库,默认情况下,docker CLI使用docker的公共注册表(public registry)。
需要说明的是:我们使用Docker的公共注册表只是因为它是免费的以及它是预先配置好的,当然您也可以使用其他的注册表,您甚至可以使用自己建立的注册表。
(1)登录Docker ID
如果您还没有Docker账户,您可以在hub.docker.com注册。然后使用您的账户登录到本地计算机上的Docker公共注册表:
1 |
$ sudo docker login |
(2)标记镜像
使用形如username/repository:tag的命名格式将本地镜像与注册表上的存储库关联。然后就可以使用您的用户名、存储库以及tag标记您的镜像并上传到相应的注册表中,命令格式如下:
1 |
$ sudo docker tag image username/reposiroty:tag |
比如,
1 |
[leo@ubuntu part02]$ sudo docker tag friendlyhello leo512/get-started:part2 |
接着,查看新标记的镜像:
1 2 3 4 5 6 |
[leo@ubuntu part02]$ sudo docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE friendlyhello latest bc9f9ece52cf 16 hours ago 132MB leo512/get-started part2 bc9f9ece52cf 16 hours ago 132MB python 2.7-slim c9cde4658340 20 hours ago 120MB hello-world latest 2cb0d9787c4d 8 weeks ago 1.85kB |
(3)发布镜像
标记完您的镜像之后,您还可以使用push命令将其上传到存储库:
1 2 3 4 5 6 7 8 9 10 |
[leo@ubuntu part02]$ sudo docker push leo512/get-started:part2 The push refers to repository [docker.io/leo512/get-started] 0f8e11f0c6a3: Pushed 14790786f3f7: Pushed 2490acae5688: Pushed d509372bacf0: Mounted from library/python 18cc3d97f405: Mounted from library/python 80db77e224a0: Mounted from library/python 8b15606a9e3e: Mounted from library/python part2: digest: sha256:b3f454032d6e53e12bedc9593e358bc604ae5823111e320cbcdfcae7334efa1a size: 1788 |
上传成功,登录到Docker Hub账号就可以看到您刚上传的新镜像了:
图3-1
(4)下载及运行镜像
此后,您就可以使用docker run命令在任何机器上来运行您的应用程序了,如果本地没有该镜像,Docker将自动从存储库中提取它并运行。比如:
1 2 3 4 5 6 7 8 |
[leo@ubuntu part02]$ sudo docker run -p 40000:80 leo512/get-started:part2 * Serving Flask app "app" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:80/ (Press CTRL+C to quit) ... |
参考:
https://docs.docker.com/get-started/part2/