Docker入门

概念
  1. image 镜像,是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,包括代码、运行的程序、库、环境变量和配置文件。
  2. container 容器是一个镜像运行时的实例 — 即镜像运行时在内存中变成的东西,默认情况下,它完全独立于主机环境运行,只在专门配置后才访问hosts file和端口。

每个容器都在独立进程中运行。

快速检查本机 docker

$ docker run hello-world

查看 docker 版本

$ docker -- version
容器

过往,当要写一个Python应用时,必须先安装Python环境,而且也会应用到你的主机环境上,同时对于运行你的应用的服务器也是这样,但是有了Docker以后,你不需要再安装Python,只需要抓取一个Python运行实例作为镜像,这样,你的构建活动就可以把Python镜像和你的代码一起工作了。

使用Dockerfile定义一个容器

Dockerfile会指明/决定在你的容器内环境中是如何配置运行的,

创建一个空文件夹,在里面创建一个名为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"]

新建两个文件 requirements.txt,app.py

requirements.txt

Flask
Redis

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=vistis)

if __name__ == "__main__":
  app.run(host='0.0.0.0', port=80)
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=vistis)

if __name__ == "__main__":
  app.run(host='0.0.0.0', port=80)

就是这样了,你的主机上不需要也不会安装 requirements.txt里的东西,看起来似乎你没有建立Python和Flask的运行环境,但实际上已经建立好了。

构建 app
# 文件目录如下 

$ ls
Dockerfile       app.py           requirements.txt
运行构建命令

这会创建一个Docker镜像,我们将要使用标签 -t 让它有一个友好的名字

$ docker build -t friendlyhello .

建好的镜像在哪呢? 它在你本机的Docker镜像库中

$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
friendlyhello       latest              29dc95b5952a        2 hours ago         148MB
运行app
$ docker run -p 4000:80 friendlyhello

* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
172.17.0.1 - - [18/Jan/2018 08:45:21] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [18/Jan/2018 08:45:21] "GET /favicon.ico HTTP/1.1" 404 -
172.17.0.1 - - [18/Jan/2018 08:46:00] "GET / HTTP/1.1" 200 -

打开浏览器进入 http://localhost:4000 可以看到结果

你也可以使用 curl 命令在命令行环境下看到相同的结果

$ curl http://localhost:4000

<h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
以分离模式 后台运行app
$ docker run -d -p 4000:80 friendlyhelloworld
查看容器进程

现在容器在后台运行,你可以使用 docker container ls 命令查看压缩后的容器ID

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
6bde28605607        friendlyhello       "python app.py"     2 minutes ago       Up 2 minutes        0.0.0.0:4000->80/tcp   zealous_heyrovsky
结束进程

使用 docker container stop 命令来结束进程,CONTAINER ID作参数

$ docker container stop 6bde28605607
共享你的镜像

一个 registry 是很多 repositories 的集合,一个 repositories 是许多 image 的集合 — 就像GiHub的仓库,默认情况下,docker CLI 使用 Docker 的公用 registry

用你的Docker ID登录
# 从你的本地主机登录到Docker公用registry
$ docker login
给镜像添加标签

将一个本地镜像和一个registry上的repository相关联的方法是 username/repository:tag ,tag是可选的,但是推荐使用,因为它是registries用来给Docker镜像赋予版本号的机制。

$ docker tag image username/repository:tag

示例:

$ docker tag friendlyhello huangke19/get-started:part2
发布镜像
$ docker push username/repository:tag

一旦完成,这次上传的结果就可以公开访问了,如果你登入Docker Hub,你将会看到你的新镜像(和它的pull命令)

从远端仓库拉取和运行镜像
$ docker run -p 4000:80 username/repository:tag

如果镜像不在本地,Docker会把它拉下来并运行

$ docker run -p 4000:80 john/get-started:part2
Unable to find image 'john/get-started:part2' locally
part2: Pulling from john/get-started
10a267c67f42: Already exists
f68a39a6a5e4: Already exists
9beaffc0cf19: Already exists
3c1fe835fb6b: Already exists
4c9f1fa8fcb8: Already exists
ee7d8f576a14: Already exists
fbccdcced46e: Already exists
Digest: sha256:0601c866aab2adcc6498200efd0f754037e909e5fd42069adeff72d1e2439068
Status: Downloaded newer image for john/get-started:part2
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

不管在哪里运行 docker run , 它都会拉取你的镜像,其中包含 Pythonrequirements.txt 里的所有依赖,然后运行代码 ,它们总是作为一个整齐干净的小包裹移动,而主机环境除了Docker完全不需要安装任何东西。