docker-php添加redis扩展

环境及版本

  • php: php:5-fpm-alpine
  • docker: version 17.06.1-ce
  • docker-compose: version 1.16.0-rc2

源码安装方式

ENV PHPREDIS_VERSION 3.1.3
RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz 
    && tar xfz /tmp/redis.tar.gz 
    && rm -r /tmp/redis.tar.gz 
    && mkdir -p /usr/src/php/ext 
    && mv phpredis-$PHPREDIS_VERSION /usr/src/php/ext/redis 
    && docker-php-ext-install redis 
    && rm -rf /usr/src/php #如果这段不加构建的镜像将大100M 

PECL安装方式

#添加扩展 redis pecl方式
RUN apk add --no-cache --update libmemcached-libs zlib
RUN set -xe 
    && apk add --no-cache --update --virtual .phpize-deps $PHPIZE_DEPS 
    && pecl install -o -f redis  
    && echo "extension=redis.so" > /usr/local/etc/php/conf.d/redis.ini
    && rm -rf /usr/share/php 
    && rm -rf /tmp/* 
    && apk del  .phpize-deps

Nginx配合docker安装nextcloud(超简易)|抛弃owncloud

Nextcloud是owncloud母公司破产前核心人员离职出来创建的,免费版里包含了很多owncloud付费版本的功能,因为核心都是一样的,所以我们当然可以选择功能更多的nextcloud版本了。

我安装的nextcloud网盘:https://nextpan.net

安装docker

在使用本教程前,建议您对docker进行一些基本的了解,知道一些简单的命令,比如:

docker images
docker ps –a
docker start
docker stop
docker rm
docker rmi

不了解docker的可以谷歌一下这些命令快速了解,也可以看官方的docker教程。

docker官方安装教程:https://store.docker.com/search?type=edition&offering=community

我贴一下Ubuntu系统的安装方式,依次输入下列命令:

sudo apt–get –y install
  apt–transport–https
  ca–certificates
  curl
curl –fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add –
sudo add–apt–repository
       “deb [arch=amd64] https://download.docker.com/linux/ubuntu
       $(lsb_release -cs)
       stable”
sudo apt–get update
sudo apt–get –y install docker–ce

安装docker-compose

docker-compose是定义和运用docker的一种工具,使用下列命令安装:

apt install docker–compose

安装完成后,新建docker-compose.yml文件:

vim docker–compose.yml

然后把下列内容复制粘贴,注意这里我选用的是wonderfall的版本,他的版本star数最多,比官方的新,另外注意,修改pwd为自己的密码:

nextcloud:
  image: wonderfall/nextcloud
  links:
    – nextcloud–db:nextcloud–db  
  environment:
    – UID=1000
    – GID=1000
    – UPLOAD_MAX_SIZE=10G
    – APC_SHM_SIZE=128M
    – OPCACHE_MEM_SIZE=128
    – CRON_PERIOD=15m
    – TZ=Aisa/Shanghai
    – ADMIN_USER=admin            
    – ADMIN_PASSWORD=pwd  
    – DOMAIN=localhost
    – DB_TYPE=mysql
    – DB_NAME=nextcloud
    – DB_USER=nextcloud
    – DB_PASSWORD=pwd
    – DB_HOST=nextcloud–db
  volumes:
    – /docker/nextcloud/data:/data
    – /docker/nextcloud/config:/config
    – /docker/nextcloud/apps:/apps2
    – /docker/nextcloud/themes:/nextcloud/themes
  ports:
    – 127.0.0.1:8888:8888/tcp
nextcloud–db:
  image: mariadb:10
  volumes:
    – /docker/nextcloud/db:/var/lib/mysql
  environment:
    – MYSQL_ROOT_PASSWORD=pwd
    – MYSQL_DATABASE=nextcloud
    – MYSQL_USER=nextcloud
    – MYSQL_PASSWORD=pwd

然后在docker—compose.yml所在文件夹里,输入以下命令:

docker–compose up –d

这时候docker版本的nextcloud就已经安装好了,端口是127.0.0.1:8888,我们接下来利用nginx反代这个端口,并且加上https,从而能让外网安全的访问。

安装nginx

apt install nginx

安装letsencrypt

apt install letsencrypt

生成证书

注意替换域名

letsencrypt certonly —webroot –w /var/www/html —domain “abc.example.com”

配置nginx

可以修改默认的,也可以新建一个配置文件,我这里直接修改默认的

vim /etc/nginx/sites–available/default

删掉里面的内容,复制粘贴进这个,注意你的letsencrypt生成的证书路径对不对,一般只需要改一下domain就行了:

server {
  listen 80;
  server_name abc.example.com;
  return 301 https://$host$request_uri;
}
server {

    server_name nextpan.net www.nextpan.net;
    listen 443 ssl http2;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/abc.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/abc.example.com/privkey.pem;
    include /etc/nginx/conf/ssl_params.conf;
    client_max_body_size 10G;
    location / {
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8888;
        proxy_set_header Host $http_host;
    }
    location = /.htaccess {
        return 404;
    }
}

重启nginx:

service nginx restart

然后输入自己的域名就可以直接登陆了。

如果想更新,只需要删掉nextcloud和mariadb的container,重新docker-compose up -d就可以了。

使用Docker配置Flask开发环境

使用Docker+docker-compose,配置最简Flask开发环境

工具

  • Docker
    一种开源容器应用,供开发者打包自己的开发环境,可以任意移植

  • docker-compose
    一种管理多个Docker容器的工具,可以简化我们启动容器的各种命令

配置文件

首先我们需要一个python基础景象,Docker各种基础镜像都可以从官方找到 https://hub.docker.com/_/python/ 。找到基础镜像之后就可以基于它做相应的配置,这些操作都记录在Dockerfile中。

Dockerfile:

FROM python:3.6-slim # 官网中挑选的python基础镜像

ADD requirements.txt requirements.txt # requirements.txt罗列了需要安装的python模块,将文件复制到容器中

RUN pip install -r ./requirements.txt # 执行模块安装

EXPOSE 5000 # 对外暴露5000端口

requirements.txt

Flask # 需要安装的python模块,如有其他需要安装的模块,如下依次写入。
# redis 
# pymongo

配置完Dockerfile之后,开始配置docker-compose文件。

docker-compose.yml

web:
  build: ../../dockerfile/python/3 # DockerFile所在目录
  ports:
    - "5000:5000" # 对外暴露端口,与Dockerfile中端口号一致
  volumes:
    - ~/workspace/python/redis:/code # 本地工作目录与容器中目录映射
  command:
    - /code/app.py # 处理请求的python脚本

app.py

#!/usr/bin/env python

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Flask Dockerized'

if __name__ == '__main__':
    app.run(debug=True,host='0.0.0.0')

容器启动

在docker-compose.yml所在文件夹执行命令 docker-compose up ,在控制台中看到如下输出:

Starting flash_web_1 ...
Starting flash_web_1 ... done
Attaching to flash_web_1
web_1  |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
web_1  |  * Restarting with stat
web_1  |  * Debugger is active!
web_1  |  * Debugger PIN: 166-703-177

同时在浏览器中输入 localhost:5000 ,并正常显示文本 Flask Dockerized ,环境就配置成功啦!对应的log都可以在启动容器的终端中查看到。例如:

web_1  | 172.17.0.1 - - [13/Sep/2017 13:18:17] "GET / HTTP/1.1" 200 -
web_1  | 172.17.0.1 - - [13/Sep/2017 13:18:17] "GET /favicon.ico HTTP/1.1" 404 -

总结

以上配置了最简的Flask开发环境,实际开发中还需要数据库、缓存、nginx等,这些基础容器都可以在Docker官网中找到,并使用docker-compose可以很清晰的将这些容器关联起来。

Ubuntu16.04安装docker

要想使用最新版本的docker需要由以下方法安装

通过docker源安装最新版本

依次输入以下命令

$ sudo apt-get install apt-transport-https
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys     36A1D7869245C8950F966E92D8576A8BA88D21E9
$ sudo bash -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
$ sudo apt-get update
$ sudo apt-get install lxc-docker

安装之后启动docker服务

$ sudo service docker start

卸载docker

卸载Docker包:

$ sudo apt-get purge docker-engine

卸载Docker包及其以来不再需要使用下面的命令:

$ sudo apt-get autoremove --purge docker-engine

上面的命令不会移除镜像,容器,卷或者是用户创建的配置文件。如果你想删除所有的镜像,容器和卷,运行下面的命令:

$ rm -rf /var/lib/docker

你必须手动删除用户创建的配置文件。

[docker] 制作base镜像并上传至docker.io

本文在Debian下完成,用于笔记:

制作base镜像

  • 安装debootstrap:
apt-get install debootstrap
  • 安装额外的软件包并制作rootfs:
debootstrap --include=openssh-server --arch i386 wheezy debian-wheezy http://httpredir.debian.org/debian
  • 打包镜像并导入到docker:
tar -c . |docker import - debian-wheezy-i386

上传至docker.io

  • 注册账号.
  • 创建空间.
vicer/debian-wheezy-i386
  • 登陆到Docker Hub:
docker login
  • 创建标签:
#docker tag <imageID> <namespace>/<image name>:<tag>
docker tag debian-wheezy-i386 vicer/debian-wheezy-i386
  • 上传镜像:
#docker push <namespace>/<image name>
docker push vicer/debian-wheezy-i386

从零开始使用 Docker 打包 Django 开发环境 (3) Docker Compose

1. 基本概念

Docker Compose 是一个用来定义和运行复杂应用的 Docker 工具。使用 Docker Compose,可以在一个文件中定义一个多容器应用,然后使用一条命令来启动你的应用,完成一切准备工作。

Docker Compose 定位是 ‘defining and running complex applications with Docker’,前身是 Fig,兼容 Fig 的模板文件。

Docker Compose 发展至今,有 Version 1、Version 2、Version 3 三个大版本。如果不声明版本,默认为 Version 1。Version 1 不能使用 volumes,、networks、 build参数。Version 2,必须在版本中申明,所有的服务,都必须申明在 service 关键字下。Version 3 删除了 volume_driver、volumes_from、cpu_shares、cpu_quota、cpuset、mem_limit、memswap_limit、extends、group_add关键字,新增了 deploy,全面支持 Swarm mode。更详细的比较可以查看参考链接。

本文中主要以 Version 2 为例学习 Docker Compose 容器的编排。

2. 工作原理

Docker Compose 将所管理的容器分为三层,工程(project),服务(service)以及容器(contaienr)。Docker Compose 运行的目录下的所有文件(docker-compose.yml、extends文件、环境变量文件等)组成一个工程,若无特殊指定工程名即为当前目录名。

一个工程当中可包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖。一个服务当中可包括多个容器实例,Docker Compose 并没有解决负载均衡的问题,因此需要借助其他工具实现服务发现及负载均衡。

Docker Compose 的工程配置文件默认为 docker-compose.yml,可通过环境变量 COMPOSE_FILE 或 -f 参数自定义配置文件,其定义了多个有依赖关系的服务及每个服务运行的容器。以下是一个简单的配置文件:

version: '2'  
services:  
    web:
        build: .
        ports:
          - "80:90"
        volumes:
          - .:/code
        links:
          - redis
    redis:
        image: redis

其定义了两个服务 web 和 redis。web 服务的镜像需要使用当前目录的 Dockerfile 实时构建,其容器运行时需要在宿主机开放端口 80 并映射到容器端口 90 ,并且挂载存储卷 /code 以及关联服务 redis。redis 服务通过镜像 redis 启动。

Docker Compose 是由 Python 语言实现的,它通过调用 docker-py 库(可参考
https://github.com/docker/docker-py )与 docker engine 通信实现构建 docker 镜像,启动停止 docker 容器等。Docker-py 库调用 docker remote API(可参考 https://docs.docker.com/reference/api/docker_remote_api/ )与 Docker Daemon 通信,可通过 DOCKER_HOST 配置本地或远程 Docker Daemon 的地址。

3. Docker Compose 常用命令

未分类

4. YAML 常用关键字

4.1 build

指定 Dockerfile 所在文件夹的路径(可以是绝对路径,或者相对 docker-compose.yml 文件的路径)。 Compose 将会利用它自动构建这个镜像,然后使用这个镜像。

build: /path/to/build/dir  

4.2 command

覆盖容器启动后默认执行的命令。

command: echo "hello world"  

4.3 dockerfile

如果需要指定额外的编译镜像的 Dockefile 文件,可以通过该指令来指定。例如:

dockerfile: Dockerfile-alternate  

注意,该指令不能跟 image 同时使用,否则 Compose 将不知道根据哪个指令来生成最终的服务镜像。

4.4 env_file

从文件中获取环境变量,可以为单独的文件路径或列表。

如果通过 docker-compose -f FILE 方式来指定 Compose 模板文件,则 env_file 中变量的路径会基于模板文件路径。

如果有变量名称与 environment 指令冲突,则按照惯例,以后者为准。

env_file: .env

env_file:  
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env

环境变量文件中每一行必须符合格式,支持 # 开头的注释行。

# common.env: Set development environment
PROG_ENV=development  

4.5 environment

设置环境变量。你可以使用数组或字典两种格式。

只给定名称的变量会自动获取运行 Compose 主机上对应变量的值,可以用来防止泄露不必要的数据。例如:

environment:  
  RACK_ENV: development
  SESSION_SECRET:

或者

environment:  
  - RACK_ENV=development
  - SESSION_SECRET

注意,如果变量名称或者值中用到 true|false,yes|no 等表达布尔含义的词汇,最好放到引号里,避免 YAML 自动解析某些内容为对应的布尔语义。

http://yaml.org/type/bool.html 中给出了这些特定词汇,包括

 y|Y|yes|Yes|YES|n|N|no|No|NO
|true|True|TRUE|false|False|FALSE
|on|On|ON|off|Off|OFF

4.6 expose

暴露端口,但不映射到宿主机,只被连接的服务访问。仅可以指定内部端口为参数

expose:  
 - "3000"
 - "8000"

4.7 image

指定为镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉取这个镜像。例如:

image: ubuntu  
image: orchardup/postgresql  
image: a4bc65fd  

4.8 links

链接到其它服务中的容器。使用服务名称(同时作为别名)或服务名称:服务别名 (SERVICE:ALIAS) 格式都可以。

links:  
 - db
 - db:database
 - redis

使用的别名将会自动在服务容器中的 /etc/hosts 里创建。例如:

172.17.2.186  db  
172.17.2.186  database  
172.17.2.187  redis  

被链接容器中相应的环境变量也将被创建。

4.9 volumes

数据卷所挂载路径设置。可以设置宿主机路径 (HOST:CONTAINER) 或加上访问模式 (HOST:CONTAINER:ro)。该指令中路径支持相对路径。例如

volumes:  
 - /var/lib/mysql
 - cache/:/tmp/cache
 - ~/configs:/etc/configs/:ro

4.10 volumes_from

从另一个服务或容器挂载它的数据卷。

volumes_from:  
 - service_name
 - container_name

从零开始使用 Docker 打包 Django 开发环境 (2) Dockerfile

1. 基本概念

Dockerfile 是一些列构建 Docker 镜像的指令集合。Docker 通过读取 Dockerfile 指令自动构建镜像。Dockerfile 类似于 Makefile,都是一种文本文件,按照构建镜像顺序组织所有的指令。

Docker 镜像的构建命令:

$ docker build .

这条命令中,Docker CLI 的处理流程如下:

  • 把当前目录及子目录当做上下文传递给 Docker Daemon
  • 从当前目录(不包括子目录)中找到 Dockerfile 文件
  • 检查 Dockerfile 的语法
  • 依次执行 Dockerfile 中的指令,根据指令生成中间过渡镜像(存储在本地,为之后的指令或构建作缓存)

2. Docker 文件组成

Dockerfile 一般包含下面几个部分:

  • 基础镜像,以哪个镜像作为基础进行制作,用法是 FROM 基础镜像名称
  • 维护者信息,需要写下该 Dockerfile 编写人的姓名或邮箱,用法是MANITAINER 名字/邮箱
  • 镜像操作命令,对基础镜像要进行的改造命令,比如安装新的软件,进行哪些特殊配置等,常见的是 RUN 命令
  • 容器启动命令,当基于该镜像的容器启动时需要执行哪些命令,常见的是 CMD 命令或 ENTRYPOINT 命令

3. Dockerfile 命令

3.1 FROM

语法:FROM image[:tag]

解释:设置要制作的镜像基于哪个镜像,FROM 指令必须是整个 Dockerfile 的第一个指令,如果指定的镜像不存在默认会自动从 Docker Hub 上下载。如果不指定 tag,默认是 latast。

3.2 MAINTAINER

语法:MAINTAINER name

解释:MAINTAINER 指令允许你给将要制作的镜像设置作者信息

3.3 RUN

语法:

  • RUN command #将会调用/bin/sh -c command
  • RUN [“executable”, “param1”, “param2”] #将会调用exec执行,以避免有些时候shell方式执行时的传递参数问题,而且有些基础镜像可能不包含/bin/sh

解释:RUN指令会在一个新的容器中执行任何命令,然后把执行后的改变提交到当前镜像,提交后的镜像会被用于Dockerfile中定义的下一步操作,RUN中定义的命令会按顺序执行并提交,这正是Docker廉价的提交和可以基于镜像的任何一个历史点创建容器的好处,就像版本控制工具一样。

3.4 CMD

语法:

  • CMD [“executable”, “param1”, “param2”] #将会调用exec执行,首选方式
  • CMD [“param1”, “param2”] #当使用ENTRYPOINT指令时,为该指令传递默认参数
  • CMD command [ param1|param2 ] #将会调用/bin/sh -c执行

解释:CMD 指令中指定的命令会在镜像运行时执行,在 Dockerfile 中只能存在一个,如果使用了多个 CMD指令,则只有最后一个 CMD 指令有效。当出现 ENTRYPOINT 指令时,CMD 中定义的内容会作为 ENTRYPOINT 指令的默认参数,也就是说可以使用 CMD 指令给 ENTRYPOINT 传递参数。

注意:RUN 和 CMD 都是执行命令,他们的差异在于 RUN 中定义的命令会在执行 docker build 命令创建镜像时执行,而 CMD 中定义的命令会在执行docker run命令运行镜像时执行,另外使用第一种语法也就是调用 exec 执行时,命令必须为绝对路径。

3.5 EXPOSE

语法:EXPOSE port [ …]

解释:EXPOSE 指令用来告诉 Docker 这个容器在运行时会监听哪些端口,Docker 在连接不同的容器(使用–link参数)时使用这些信息。

3.6 ENV

语法:ENV key value

解释:ENV 指令用于设置环境变量,在 Dockerfile 中这些设置的环境变量也会影响到 RUN 指令,当运行生成的镜像时这些环境变量依然有效,如果需要在运行时更改这些环境变量可以在运行 docker run 时添加–env key=value参数来修改。

注意:最好不要定义那些可能和系统预定义的环境变量冲突的名字,否则可能会产生意想不到的结果。

3.7 ADD

语法:ADD src dest

解释:ADD 指令用于从指定路径拷贝一个文件或目录到容器的指定路径中,是一个文件或目录的路径,也可以是一个 url,路径是相对于该 Dockerfile 文件所在位置的相对路径, 是目标容器的一个绝对路径,例如 /home/yooke/Docker/Dockerfile 这个文件中定义的,那么 ADD /data.txt /db/指令将会尝试拷贝文件从 /home/yooke/Docker/data.txt 到将要生成的容器的 /db/data.txt,且文件或目录的属组和属主分别为 uid 和 gid 为0的用户和组,如果是通过 url 方式获取的文件,则权限是600。

注意:

  • 如果执行 docker build somefile 即通过标准输入来创建时,ADD 指令只支持 url 方式,另外如果 url 需要认证,则可以通过 RUN wget … 或 RUN curl … 来完成,ADD 指令不支持认证。
  • src 路径必须与 Dockerfile 在同级目录或子目录中,例如不能使用ADD ../somepath,因为在执行docker build时首先做的就是把 Dockerfile 所在目录包含子目录发送给 docker 的守护进程。
  • 如果 src 是一个 url 且 dest 不是以 ‘/’ 结尾,则会下载文件并重命名为 dest 。
  • 如果 src 是一个 url 且 dest 以 ‘/’ 结尾,则会下载文件到 dest filename,url 必须是一个正常的路径形式,’http://example.com’ 像这样的 url 是不能正常工作的。
  • 如果 src 是一个本地的压缩包且 dest 是以 ‘/’ 结尾的目录,则会调用 ‘tar -x’ 命令解压缩,如果 dest 有同名文件则覆盖,但 src 是一个 url 时不会执行解压缩。

3.8 COPY

语法:COPY src dest

解释:用法与 ADD 相同,不过 src 不支持使用url,所以在使用 docker build somefile 时该指令不能使用。

3.9 ENTRYPOINT

语法:

  • ENTRYPOINT [‘executable’, ‘param1’, ‘param2’] #将会调用exec执行,首选方式
  • ENTRYPOINT command param1 param2 #将会调用/bin/sh -c执行

解释:ENTRYPOINT 指令中指定的命令会在镜像运行时执行,在 Dockerfile 中只能存在一个,如果使用了多个 ENTRYPOINT 指令,则只有最后一个指令有效。ENTRYPOINT 指令中指定的命令(exec执行的方式)可以通过 docker run 来传递参数,例如 docker run images -l 启动的容器将会把 -l 参数传递给 ENTRYPOINT 指令定义的命令并会覆盖 CMD 指令中定义的默认参数(如果有的话),但不会覆盖该指令定义的参数,例如 ENTRYPOINT [‘ls’,’-a’],CMD [‘/etc’],当通过 docker run image 启动容器时该容器会运行 ls -a /etc 命令,当使用 docker run image -l 启动时该容器会运行 ls -a -l 命令,-l 参数会覆盖 CMD 指令中定义的/etc参数。

注意:

  • 当使用 ENTRYPOINT 指令时生成的镜像运行时只会执行该指令指定的命令。
  • 当出现 ENTRYPOINT 指令时 CMD 指令只可能(当 ENTRYPOINT 指令使用 exec 方式执行时)被当做 ENTRYPOINT 指令的参数使用,其他情况则会被忽略。

3.10 VOLUME

语法:VOLUME [‘samepath’]

解释:VOLUME 指令用来设置一个挂载点,可以用来让其他容器挂载以实现数据共享或对容器数据的备份、恢复或迁移。

3.11 USER

语法:USER [username|uid]

解释:USER指令用于设置用户或uid来运行生成的镜像和执行 RUN 指令。

3.12 WORKDIR

语法:WORKDIR /path/to/workdir

解释:WORKDIR 指令用于设置 Dockerfile 中的 RUN、CMD 和 ENTRYPOINT 指令执行命令的工作目录(默认为/目录),该指令在 Dockerfile 文件中可以出现多次,如果使用相对路径则为相对于 WORKDIR 上一次的值,例如 WORKDIR /data,WORKDIR logs,RUN pwd 最终输出的当前目录是 /data/logs。

4. 最佳实践

4.1 使用 .dockerignore 文件

在 docker 构建镜像的第一步,docker CLI 会先在上下文目录中寻找 .dockerignore 文件,根据 .dockerignore 文件排除上下文目录中的部分文件和目录,然后把剩下的文件和目录传递给 docker 服务。.dockerignore 语法同 .gitignore。

4.2 避免安装不必要的包

为了减小复杂度、依赖、文件大小和创建的时间,应该避免安装额外或者不必要的包。例如,我们不必在一个数据库镜像中包含一个文本编辑器。

4.3 对于多行参数要做字典序排序

只要有可能,通过对多行参数进行字母数字排序来缓解后续变更。这将帮助你避免重复的包并且更容易更新。在反斜线( )前添加一个空格是个好习惯。

RUN apt-get update && apt-get install -y   
  bzr 
  cvs 

4.4 尽量利用 build 镜像的缓存

未分类

在创建镜像过程中,Docker 将按照 Dockerfile 指定步骤执行每个指令。 一般情况下,对于每条命令,docker 都会生成一层镜像。如果在构建某个镜像层的时候,发现这个镜像层已经存在了,就直接使用,而不是重新构建。

大部分指令是通过与缓存进行对比该指令、执行指令的基础镜像,判断是否使用缓存。除了 ADD 和 COPY,这两个指令会复制文件内容到镜像内,docker 还会检查每个文件内容校验和(不包括最后修改时间和最后访问时间),如果校验和不一致,则不会使用缓存。

4.5 每个镜像只有一个功能

不要在容器里运行多个不同功能的进程,每个镜像中只安装一个应用的软件包和文件,需要交互的程序通过 pod(kubernetes 提供的特性) 或者容器之间的网络进行通信。这样可以保证模块化,不同的应用可以分开维护和升级,也能减小单个镜像的大小。

4.6 不要在构建中升级版本

更新将发生在基础镜像里,不要在你的容器内来apt-get upgrade更新。因为在隔离情况下,如果更新时试图修改 init 或改变容器内的设备,更新可能会经常失败。它还可能会产生不一致的镜像,因为你不再有你的应用程序该如何运行以及包含在镜像中依赖的哪种版本的正确源文件。

从零开始使用 Docker 打包 Django 开发环境 (1) 环境搭建

Vagrant 适合用来管理虚拟机,而 Docker 适合用来管理应用环境。为了更好地模拟真实运行环境,本系列文章借助 Docker 和 Docker Compose 搭建 Nginx + uWSGI+ Django + MySQL + Redis + Rabbit 的开发环境。

1. 基本概念

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。Docker 可以让开发者打包应用以及依赖包到一个轻量级、可移植的容器中,然后发布到机器上。

Docker Compose 是一个用来定义和运行复杂应用的 Docker 工具。

2. Docker 安装和使用

2.1 安装

在 Linux 上 安装 Docker。

# curl -sSL https://get.daocloud.io/docker | sh

在 Linux 上 安装 Docker Compose。

# curl -L https://get.daocloud.io/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose

2.2 查看安装信息

安装完毕后,可以通过如下命令检测是否安装成功。

查看 Docker 版本

# docker --version 
Docker version 1.12.6, build 88a4867/1.12.6  

查看 docker-compose 版本

# docker-compose --version
docker-compose version 1.16.1, build 6d1ac21  

拉起 Docker Deamon。Docker Client 需要将 Console 输入的命令,发送给 Daemon 执行。

# service docker start

通过,docker info 命令可以看到,本地使用的是 docker 原生的镜像源。

# docker info // 查看 docker 信息
Docker Root Dir: /var/lib/docker  
Debug Mode (client): false  
Debug Mode (server): false  
Registry: https://index.docker.io/v1/  
Insecure Registries:  
 127.0.0.0/8
Registries: docker.io (secure)  

2.3 拉取、启动镜像

拉取 hello-world 镜像。

# docker pull hello-world
Using default tag: latest  
Trying to pull repository docker.io/library/hello-world ...  
latest: Pulling from docker.io/library/hello-world

Digest: sha256:f3b3b28a45160805bb16542c9531888519430e9e6d6ffc09d72261b0d26ff74f  

启动 hello-world 镜像。

# docker run hello-world

Hello from Docker!  
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:  
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:  
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:  
 https://cloud.docker.com/

For more examples and ideas, visit:  
 https://docs.docker.com/engine/userguide/

查看本地镜像列表。

# docker images 
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE  
docker.io/hello-world   latest              1815c82652c0        12 weeks ago        1.84 kB  

2.4 配置加速器(可选)

使用 Docker 的时候,经常需要从官方获取镜像,但是由于网络原因,拉取镜像的过程非常耗时,严重影响使用 Docker 的体验。

这里使用的 DaoDlcoud 提供的加速器,通过智能路由和缓存机制,提升了访问 Docker Hub 的速度。

curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://6c5fbccc.m.daocloud.io

Docker的save和export命令的区别

Docker是如何工作的(简单说明)

Docker是基于镜像的。镜像类似于已经包含了文件、配置和安装好的程序的虚拟机镜像。同样的,你可以像启动虚拟机一样启动多个镜像实例。运行中的镜像称为容器。你可以修改容器(比如删除一个文件),但这些修改不会影响到镜像。不过,你使用 docker commit 命令可以把一个正在运行的容器变成一个新的镜像。

举个例子:

# 像Docker官方的hello world例子一样,拉取一个叫busybox的镜像

sudo docker pull busybox



# 查看本地已经有哪些镜像

# 我们可以看到busybox

sudo docker images



# 现在让我们来修改下busybox镜像的容器

# 这次,我们创建一个文件夹

sudo docker run busybox mkdir /home/test



# 让我们再看看我们有哪些镜像了。

# 注意每条命令执行后容器都会停止

# 可以看到有一个busybox容器

sudo docker ps -a



# 现在,可以提交修改了。

# 提交后会看到一个新的镜像busybox-1

#  <CONTAINER ID> 是刚刚修改容器后得到的ID

sudo docker commit <CONTAINER ID> busybox-1



# 再看看我们有哪些镜像。

# 我们现在同时有busybox和busybox-1镜像了。

sudo docker images



# 我们执行以下命令,看看这两个镜像有什么不同

sudo docker run busybox [ -d /home/test ] && echo 'Directory found' || echo 'Directory not found'



sudo docker run busybox-1 [ -d /home/test ] && echo 'Directory found' || echo 'Directory not found'

现在,我们有两个不同的镜像了(busybox和busybox-1),还有一个通过修改busybox容器得来的容器(多了一个/home/test文件夹)。下面来看看,是如何持久化这些修改的。

导出(Export)

Export命令用于持久化容器(不是镜像)。所以,我们就需要通过以下方法得到容器ID:

sudo docker ps -a

接着执行导出:

sudo docker export <CONTAINER ID> > /home/export.tar

最后的结果是一个2.7MB大小的Tar文件(比使用save命令稍微小些)。

保存(Save)

Save命令用于持久化镜像(不是容器)。所以,我们就需要通过以下方法得到镜像名称:

sudo docker images

接着执行保存:

sudo docker save busybox-1 > /home/save.tar

最后的结果是一个2.8MB大小的Tar文件(比使用export命令稍微大些)。

它们之间的不同

现在我们创建了两个Tar文件,让我们来看看它们是什么。首先做一下小清理——把所有的容器和镜像都删除:

# 查看所有的容器

sudo docker ps -a


# 删除它们

sudo docker rm <CONTAINER ID>


# 查看所有的镜像

sudo docker images


# 删除它们

sudo docker rmi busybox-1

sudo docker rmi busybox

译注:可以使用 docker rm $(docker ps -q -a) 一次性删除所有的容器,docker rmi $(docker images -q) 一次性删除所有的镜像。

现在开始导入刚刚导出的容器:

# 导入export.tar文件

cat /home/export.tar | sudo docker import - busybox-1-export:latest


# 查看镜像

sudo docker images


# 检查是否导入成功,就是启动一个新容器,检查里面是否存在/home/test目录(是存在的)

sudo docker run busybox-1-export [ -d /home/test ] && echo 'Directory found' || echo 'Directory not found'

使用类似的步骤导入镜像:

# 导入save.tar文件

docker load < /home/save.tar


# 查看镜像

sudo docker images


# 检查是否导入成功,就是启动一个新容器,检查里面是否存在/home/test目录(是存在的)

sudo docker run busybox-1 [ -d /home/test ] && echo 'Directory found' || echo 'Directory not found'

那,它们之间到底存在什么不同呢?我们发现导出后的版本会比原来的版本稍微小一些。那是因为导出后,会丢失历史和元数据。执行下面的命令就知道了:

# 显示镜像的所有层(layer)

sudo docker images --tree

执行命令,显示下面的内容。正你看到的,导出后再导入(exported-imported)的镜像会丢失所有的历史,而保存后再加载(saveed-loaded)的镜像没有丢失历史和层(layer)。这意味着使用导出后再导入的方式,你将无法回滚到之前的层(layer),同时,使用保存后再加载的方式持久化整个镜像,就可以做到层回滚(可以执行 docker tag 来回滚之前的层)。

vagrant@ubuntu-13:~$ sudo docker images --tree

 ├─f502877df6a1 Virtual Size: 2.489 MB Tags: busybox-1-export:latest

 └─511136ea3c5a Virtual Size: 0 B

  └─bf747efa0e2f Virtual Size: 0 B

 └─48e5f45168b9 Virtual Size: 2.489 MB

  └─769b9341d937 Virtual Size: 2.489 MB

 └─227516d93162 Virtual Size: 2.489 MB Tags: busybox-1:latest

以下部分内容来自于:大桥下的蜗牛的Docker 问答录(100 问),更多有关Docker相关常见问题可参考:https://blog.lab99.org/post/docker-2016-07-14-faq.html

随着镜像分层平面化后,docker images –tree 这个命令就取消了。幸运的是,Nate Jones 写了一个工具,用于可视化镜像分层依赖,叫做 dockviz:https://github.com/justone/dockviz

对于 Mac 平台的用户,可以很方便的使用 brew 来进行安装:

brew install dockviz

对于其它平台的用户,可以直接去发布页面下载。

安装好后,直接执行 dockviz images –tree 即可:

├─<missing> Virtual Size: 1.1 MB

│ └─<missing> Virtual Size: 1.1 MB

│   └─176825169704 Virtual Size: 1.1 MB Tags: busybox-1:latest

├─616f38fe120d Virtual Size: 1.1 MB Tags: busybox-1-export:latest

在新版本Docker中也可以使用 docker history 命令来查看镜像历史。

未分类

Docker在PHP中的实践过程

最近微服务很火,很多人都在尝试,我们公司也在这段时间尝试着来时间微服务化,其中就涉及到Docker。
在实践docker中踩了很多坑,也对Docker有了更多的认识,下面记录一下。

Docker在打包Spring boot项目时候,因为Spring boot内部集成了tomcat并且提供了直接打包成jar包的方式,Spring boot如何打包: https://docs.spring.io/spring-boot/docs/current/reference/html/build-tool-plugins-maven-plugin.html

Dockerfile文件如下:

FROM java:8

COPY target/your-project-name.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]

因为公司中还有一个之前的PHP项目,所以也需要对PHP进行打包,查了很多资料,最后在实践中发现,PHP项目需要一个nginx来管理网络,用如何的Dockerfile文件打包的话就会有问题。

FROM php:7.0-cli
RUN apt-get update && apt-get install -y 
        libfreetype6-dev 
        libjpeg62-turbo-dev 
        libmcrypt-dev 
        libpng12-dev 
    && docker-php-ext-install -j$(nproc) iconv mcrypt 
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ 
    && docker-php-ext-install -j$(nproc) gd
FROM php:7.1-fpm
RUN pecl install -o -f redis 
    &&  rm -rf /tmp/pear 
    &&  docker-php-ext-enable redis
RUN docker-php-ext-install  mysqli pdo pdo_mysql

EXPOSE 80

COPY . /php
WORKDIR /php

经过再三查找资料,发现这么一篇文章:

Dockerise your PHP application with Nginx and PHP7-FPM

在里面找到了解决办法,使用docker-compose就好了,docker-compose.yml文件如下:

version: '2'

services:
    web:
        image: nginx:latest
        ports:
            - "8080:80"
        volumes:
            - ./:/php
            - ./site.conf:/etc/nginx/conf.d/default.conf
        networks:
            - code-network
    php:
        image: php:fpm
        volumes:
            - ./:/php
        networks:
            - code-network

networks:
    code-network:
        driver: bridge

上面提到的site.conf文件如下:

server {
    listen 80;
    index index.php index.html;
    server_name localhost;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /php;

    location ~ .php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

直接在命令行执行:

$sudo docker-compose up

就可以看看到运行的结果了,可是因为我们是直接部署到阿里云容器服务上面的,docker-compose这种方式就有了缺陷,因为阿里云容器服务不支持server这种方式,不得已最终经过查询,找到了:
https://hub.docker.com/r/richarvey/nginx-php-fpm/

直接使用如下Dockerfile就可以了:

FROM richarvey/nginx-php-fpm:latest

MAINTAINER Ric Harvey <[email protected]>

ENV PHPREDIS_VERSION 3.0.0
RUN mkdir -p /usr/src/php/ext/redis 
    && curl -L https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz | tar xvz -C /usr/src/php/ext/redis --strip 1 
    && echo 'redis' >> /usr/src/php-available-exts 
    && docker-php-ext-install redis

EXPOSE 80

ADD ./ /var/www/html/

既然当初选择了Docker这种方式,就要想办法吃透这门技术,否则在生产环境中的话出了问题就麻烦了。以上对与PHP的Docker化的实践过程,盯着一行行的日志信息查找原因,因为对PHP不是特别熟,还向PHP的同事请教了很多问题。

记录下来面的日后再遇到,虽然以上内容没有针对每个Docker的技术点进行介绍,找时间把每个技术点都记录下来。简单来说就是,PHP必须要搭配nginx使用。