Docker:說愛你不容易

docker 對於不少程序猿來講,一點都不陌生,畢竟它是一個輕量級的部署神器。php

也許,也有不少童鞋和我同樣,只據說過,卻沒有真正的實踐過 docker。那麼,如今一塊兒走進 docker 的世界。html

什麼是 Docker?

Docker 概念

Docker is an open-source project that automates the deployment of applications inside software containers, by providing an additional layer of abstraction and automation of operating-system-level virtualization on Linux. Docker uses the resource isolation features of the Linux kernel such as cgroups and kernel namespaces, and a union-capable filesystem such as aufs and others to allow independent 「containers」 to run within a single Linux instance, avoiding the overhead of starting and maintaining virtual machines.mysql

Docker是一個開放源代碼軟件項目,讓應用程序佈署在軟件容器下的工做能夠自動化進行,藉此在Linux操做系統上,提供一個額外的軟件抽象層,以及操做系統層虛擬化的自動管理機制。Docker利用Linux核心中的資源分離機制,例如cgroups,以及Linux核心命名空間(name space),來創建獨立的軟件容器(containers)。這能夠在單一Linux實體下運做,避免啓動一個虛擬機器形成的額外負擔。linux

——摘自維基百科nginx

Docker 和虛擬機(VM)的區別

一張圖歸納vm和docker的架構區別。具體的能夠查看 知乎 - docker容器與虛擬機有什麼區別?git

圖片描述

總之明確一點, Docker 不是虛擬機github

Docker 的應用場景

  • Web 應用的自動化打包和發佈。
  • 自動化測試和持續集成、發佈。
  • 在服務型環境中部署和調整數據庫或其餘的後臺應用。

爲何要用 Docker?

Docker 的優勢

Docker automates the repetitive tasks of setting up and configuring development environments so that developers can focus on what matters: building great software.web

Docker自動執行設置和配置開發環境的重複任務,以便開發人員能夠專一於重要的事情:構建出優秀的軟件。sql

Developers using Docker don’t have to install and configure complex databases nor worry about switching between incompatible language toolchain versions.docker

使用Docker的開發人員沒必要安裝和配置複雜的數據庫,也不用擔憂在不兼容版本之間切換。

  1. 簡化配置
  2. 代碼流水線(Code Pipeline)管理
  3. 提升開發效率
  4. 隔離應用
  5. 整合服務器
  6. 調試能力Docker
  7. 多租戶環境
  8. 快速部署

-- 來源於 DockOne.io - 八個Docker的真實應用場景

獲取 Docker

安裝

  1. 官方安裝。 傳送門
  2. 經過阿里雲鏡像倉庫安裝

先登陸 阿里雲 - 開發者平臺,登陸後進入管理中心,點擊管理中心中的 Docker Hub 鏡像站點

圖片描述

  1. 經過 DaoCloud 安裝。 傳送門

Docker 加速

目前國內比較多人用的加速器有 DaoCloud 和 阿里雲。

  1. 阿里雲

先登陸 阿里雲 - 開發者平臺,登陸後進入管理中心,點擊管理中心中的 Docker Hub 鏡像站點

圖片描述

能夠看到控制檯中的專屬加速器。

  1. DaoCloud

    登陸後打開 [DaoCloud - 加速器](https://www.daocloud.io/mirror#accelerator-doc)便可看到配置 docker 加速器的腳本,拷貝代碼運行便可。

非root用戶使用 docker

建立docker組: sudo groupadd docker

將當前用戶加入docker組: sudo gpasswd -a ${USER} docker

從新啓動docker服務:sudo systemctl restart docker

當前用戶退出系統從新登錄便可。

認識 Docker

查看Docker安裝狀況

Docker 安裝以後,可使用 docker -v 查看docker 版本。

{17-09-19 10:59}Leung:~ lynnleung% docker -v
Docker version 17.06.2-ce, build cec0b72

啓動第一個docker: hello-world 。

{17-09-19 11:00}Leung:~ lynnleung% docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
5b0f327be733: Pull complete
Digest: sha256:1f19634d26995c320618d94e6f29c09c6589d5df3c063287a00e6de8458f8242
Status: Downloaded newer image for hello-world:latest

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經常使用命令

pull

做用:從鏡像倉庫中拉取或者更新指定鏡像

語法: docker pull [OPTIONS] NAME[:TAG|@DIGEST]

options:

  • -a:拉取全部tagged鏡像
  • --disable-content-trust :忽略鏡像的校驗,默認開啓

TAG 默認爲 latest,即拉取倉庫最新的鏡像。具體的tag值能夠去官方hub中查看。

舉個栗子,拉取nginx鏡像,未指定tag時,默認拉取最新版本(在 Docker Hub - Nginx鏡像首頁能夠看到,最新的nginx版本爲 1.13.5):

{17-09-19 10:57}Leung:~ lynnleung% docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
afeb2bfd31c0: Pull complete
7ff5d10493db: Pull complete
d2562f1ae1d0: Pull complete
Digest: sha256:aa1c5b5f864508ef5ad472c45c8d3b6ba34e5c0fb34aaea24acf4b0cee33187e
Status: Downloaded newer image for nginx:latest

先把鏡像拉取下來,待會就能夠直接使用。

run

做用:建立一個新的容器,並運行一個命令

語法:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

經常使用的options:

  • -a stdin:指定標準輸入輸出內容類型,可選 STDIN/STDOUT/STDERR 三項
  • -d: 後臺運行容器,並返回容器ID
  • -i: 以交互模式運行容器,一般與 -t 同時使用
  • -t: 爲容器從新分配一個僞輸入終端,一般與 -i 同時使用
  • --name="my-nginx": 爲容器指定一個名稱
  • -h "hostname;": 指定容器的hostname
  • -e username="ritchie": 設置環境變量
  • --link=[]: 添加連接到另外一個容器
  • --expose=[]: 開放一個端口或一組端口
  • -p hostport:containerport:指定容器暴露的端口對應宿主機的端口
  • -P:暴露容器端口對應宿主機的隨機端口
  • -v :給容器掛載存儲卷,掛載到容器的某個目錄

舉個栗子,運行一個nginx:

{17-09-19 11:32}Leung:~ lynnleung% docker run -P -d nginx
21ad408c79947d1cbee7540b1ae1586987acfc4bb6b09b6339b02879c70aeb2e
{17-09-19 11:32}Leung:~ lynnleung% docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                   NAMES
21ad408c7994        nginx               "nginx -g 'daemon ..."   2 seconds ago       Up 1 second                 0.0.0.0:32768->80/tcp   hopeful_clarke
bd8dc013d734        hello-world         "/hello"                 31 minutes ago      Exited (0) 31 minutes ago                           clever_bohr

能夠看到 PORTS,容器中的 80 端口對應了宿主機中的 32768 端口,此時訪問 http://localhost:32768 便可看到nginx的默認頁面。

圖片描述

若是在虛擬機中運行 docker run -d -p 80:80 nginx 訪問虛擬機的地址 http://10.211.55.9,一樣能夠打開nginx的默認頁面。

leung@ubuntu:~$ docker run -d -p 80:80 nginx
8b15325246d29c7b6f50cd3290c7a91fdf4b7d78240779720a2abdc64555ab45
leung@ubuntu:~$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS                NAMES
8b15325246d2        nginx               "nginx -g 'daemon ..."   18 hours ago        Up 18 hours               0.0.0.0:80->80/tcp   flamboyant_mayer

start/stop/restart

做用:

docker start :啓動一個或多少已經被中止的容器

docker stop :中止一個運行中的容器

docker restart :重啓容器

語法:

docker start [OPTIONS] CONTAINER [CONTAINER...]
docker stop [OPTIONS] CONTAINER [CONTAINER...]
docker restart [OPTIONS] CONTAINER [CONTAINER...]

舉個栗子,中止虛擬機中的nginx:

leung@ubuntu:~$ docker stop flamboyant_mayer
flamboyant_mayer
leung@ubuntu:~$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
8b15325246d2        nginx               "nginx -g 'daemon ..."   18 hours ago        Exited (0) 3 seconds ago                       flamboyant_mayer

此時再訪問虛擬機ip已是不能訪問了。

圖片描述

再運行 docker start flamboyant_mayer能夠再次啓動nginx。

exec

做用:在運行的容器中執行命令

語法:docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

options:

  • -d :分離模式: 在後臺運行
  • -i :即便沒有附加也保持STDIN 打開
  • -t :分配一個僞終端

舉個例子,進入虛擬機中的nginx容器查看nginx的版本和已安裝模塊:

leung@ubuntu:~$ docker exec -it flamboyant_mayer nginx -v
nginx version: nginx/1.13.5

leung@ubuntu:~$ docker exec -i -t flamboyant_mayer /bin/bash
root@8b15325246d2:/# nginx -V
nginx version: nginx/1.13.5
built by gcc 6.3.0 20170516 (Debian 6.3.0-18)
built with OpenSSL 1.1.0f  25 May 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.13.5/debian/debuild-base/nginx-1.13.5=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-specs=/usr/share/dpkg/no-pie-link.specs -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'
root@8b15325246d2:/#

能夠看到,最新版的nginx容器確實是 1.13.5 版本,而且安裝了大部分經常使用的模塊。

create

做用: 建立一個新的容器可是不啓動它

語法: docker create [OPTIONS] IMAGE [COMMAND] [ARG...] 語法同 run

舉個栗子:

leung@ubuntu:~$ docker create -p 8888:80 --name create_nginx nginx
ac3eca0321b872109b10444b4080b45f2b13333b8c265b4ec77fff05fc25ff61
leung@ubuntu:~$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS                NAMES
ac3eca0321b8        nginx               "nginx -g 'daemon ..."   6 seconds ago       Created                                        create_nginx
8b15325246d2        nginx               "nginx -g 'daemon ..."   18 hours ago        Up 10 minutes             0.0.0.0:80->80/tcp   flamboyant_mayer
leung@ubuntu:~$ docker start create_nginx
create_nginx

運行 start 命令後,便可訪問虛擬機的 8888 端口。

build

做用: 根據 Dockerfile 構建鏡像。

語法: docker build [OPTIONS] PATH | URL | -

經常使用的 options:

  • -f :指定要使用的Dockerfile路徑。
  • --force-rm :設置鏡像過程當中刪除中間容器。
  • -q :安靜模式,成功後只輸出鏡像ID。
  • --no-cache :建立鏡像的過程不使用緩存。
  • --rm :設置鏡像成功後刪除中間容器。

ps

做用: 列出容器

語法: create

options:

  • -a :顯示全部的容器,包括未運行的
  • -f :根據條件過濾顯示的內容
  • --format :指定返回值的模板文件
  • -l :顯示最近建立的容器
  • -n :列出最近建立的n個容器
  • --no-trunc :不截斷輸出
  • -q :靜默模式,只顯示容器編號
  • -s :顯示總的文件大小
leung@ubuntu:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
8b15325246d2        nginx               "nginx -g 'daemon ..."   18 hours ago        Up 8 minutes        0.0.0.0:80->80/tcp   flamboyant_mayer
leung@ubuntu:~$ docker ps -q
8b15325246d2
leung@ubuntu:~$ docker ps -s
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES               SIZE
8b15325246d2        nginx               "nginx -g 'daemon ..."   18 hours ago        Up 8 minutes        0.0.0.0:80->80/tcp   flamboyant_mayer    16B (virtual 108MB)
leung@ubuntu:~$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS                NAMES
ac3eca0321b8        nginx               "nginx -g 'daemon ..."   6 seconds ago       Created                                        create_nginx
8b15325246d2        nginx               "nginx -g 'daemon ..."   18 hours ago        Up 10 minutes             0.0.0.0:80->80/tcp   flamboyant_mayer

port

做用: 列出指定的容器的端口映射,或者查找將PRIVATE_PORT NAT到面向公衆的端口。

語法: docker port [OPTIONS] CONTAINER [PRIVATE_PORT[/PROTO]]

栗子:

leung@ubuntu:~$ docker port flamboyant_mayer
80/tcp -> 0.0.0.0:80

top

做用: 查看容器中運行的進程信息,支持 ps 命令參數。

語法: docker top [OPTIONS] CONTAINER [ps OPTIONS]

栗子:

leung@ubuntu:~$ docker top flamboyant_mayer
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                1999                1981                0                   11:50               ?                   00:00:00            nginx: master process nginx -g daemon off;
systemd+            2028                1999                0                   11:50               ?                   00:00:00            nginx: worker process

images

做用: 列出本地鏡像

語法: docker images [OPTIONS] [REPOSITORY[:TAG]]

經常使用的options:

  • -a :列出本地全部的鏡像(含中間映像層,默認狀況下,過濾掉中間映像層)
  • -f :顯示知足條件的鏡像
  • -q :只顯示鏡像ID
leung@ubuntu:~$ docker images -a
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              66216d141be6        10 days ago         108MB

rm/rmi

用法: rm 用於刪除一個或多個容器rmi 用於刪除一個或多個本地鏡像。

語法:

rm: docker rm [OPTIONS] CONTAINER [CONTAINER...]

rm 的options:

  • -f :經過SIGKILL信號強制刪除一個運行中的容器
  • -l :移除容器間的網絡鏈接,而非容器自己
  • -v:-v 刪除與容器關聯的卷

rmi: docker rmi [OPTIONS] IMAGE [IMAGE...]

rmi 的options:

  • -f :強制刪除
  • --no-prune :不移除該鏡像的過程鏡像,默認移除

tag

做用: 標記本地鏡像,將其納入某一倉庫。

語法: docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]

要注意這裏的 username 指的是你註冊docker hub後,在hub上的惟一標識符,也是登陸的 username

舉個栗子:docker tag nginx leungjz/nginx

push

做用: 將本地的鏡像上傳到鏡像倉庫,要先登錄到鏡像倉庫

Linux登陸倉庫的方法是: 運行 docker login 命令,而後輸入dockerhub的登陸名和密碼便可,登陸名不是郵箱。

Mac 登陸倉庫爲點擊狀態欄上的小鯨魚,在下拉欄中選擇 Sign in 登陸便可。

{17-09-19 14:28}Leung:~ lynnleung% docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don‘t have a Docker ID, head over to https://hub.docker.com to create one.
Username: leungjz
Password:
Login Succeeded

語法: docker push [OPTIONS] NAME[:TAG]

options:

  • --disable-content-trust :忽略鏡像的校驗,默認開啓

在push前,須要標記本地某個鏡像,舉個栗子(Mounted 那裏應爲是基於個人測試帳號中tag過來。):

{17-09-19 14:32}Leung:~ lynnleung% docker tag nginx leungjz/nginx
{17-09-19 14:33}Leung:~ lynnleung% docker push leungjz/nginx
The push refers to a repository [docker.io/leungjz/nginx]
110566462efa: Mounted from buct132/nginx
305e2b6ef454: Mounted from buct132/nginx
24e065a5f328: Mounted from buct132/nginx
latest: digest: sha256:d8565c25b654da69bc9b837a0dee713c988f0276e90564aa8fd12ebf4c2ff11e size: 948

commit

做用: 從容器建立一個新鏡像。

語法: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

options:

  • -a :提交的鏡像做者
  • -c :使用Dockerfile指令來建立鏡像
  • -m :提交時的說明文字
  • -p :在commit時,將容器暫停

舉個栗子:

{17-09-19 14:48}Leung:~ lynnleung% docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
6b06e2bb098e        nginx               "nginx -g 'daemon ..."   38 minutes ago      Up 38 minutes       10.211.55.2:80->80/tcp   nginx
{17-09-19 14:48}Leung:~ lynnleung% docker commit -a "leungjz" -m "my nginx" 6b06 leungjz/nginx
sha256:3b4cb2b72b78be5da995e42ad051cbf0c73fd62fd437ac56469d8ee8f488b4c6
{17-09-19 14:49}Leung:~ lynnleung% docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
leungjz/nginx       latest              3b4cb2b72b78        6 seconds ago       108MB
nginx               latest              da5939581ac8        5 days ago          108MB
hello-world         latest              05a3bd381fc2        6 days ago          1.84kB
{17-09-19 14:49}Leung:~ lynnleung% docker push leungjz/nginx
The push refers to a repository [docker.io/leungjz/nginx]
a13795f76641: Pushed
110566462efa: Mounted from buct132/nginx
305e2b6ef454: Mounted from buct132/nginx
24e065a5f328: Layer already exists
latest: digest: sha256:eaa0d73ad93f9ce2ebe8df0059d028742bcf1c92aeaebf65e9c2a9b3558f7acd size: 1155

使用 Docker 搭建 lnmp 環境

既然 Docker 能夠直接啓動 nginx 服務器,那麼也確定能夠快速搭建 lnmp 環境。

爲了方便管理,先新建一個項目目錄:

$ mkdir -p ./docker/mysql ./docker/php ./docker/nginx ./docker/php ./docker/project
$ cd docker

其中,

  • mysql 目錄用於存放 mysql 的數據。
  • php 目錄用於存放 php.ini 配置文件
  • nginx 目錄用於存放 nginx 中的網站配置文件。
  • project 目錄用於存放項目文件。

啓動 MySql

一條命令便可快速啓動一個mysql服務器:

docker run -p 3306:3306 -v $PWD/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d --name mysql mysql

運行後,docker 會自動從倉庫中下載最新的 mysql 鏡像,並設置爲 root 密碼爲 123456 ,同時暴露出 3306 端口,容器命名爲 mysql,並後臺運行。

啓動 PHP-FPM

這裏的 php 使用 fpm 管理進程:
先新建一個 php.ini :

$ touch php/php.ini

配置文件中的配置項能夠自行設置。

docker run -p 9000:9000 -v $PWD/project:/usr/share/nginx/html -v $PWD/php/php.ini:/usr/local/etc/php/conf.d/php.ini --link mysql:mysql -d --name php7-fpm php:7.1-fpm

從官方的 php7.1-fpm 爲基礎容器。由於要鏈接數據庫,因此還得進入容器中安裝 php 的 pdo_mysql 模塊。

leung@ubuntu:~$ docker exec -i -t php7-fpm /bin/bash
root@d254489bd9e4:/var/www/html# cd /usr/local/bin/
root@d254489bd9e4:/usr/local/bin# ./docker-php-ext-install pdo_mysql
...安裝過程省略...
root@d254489bd9e4:/usr/local/bin# php -m
...
PDO
pdo_mysql
pdo_sqlite
....

能夠看到,已經安裝上了 pdo_mysql 的模塊。重啓 php 容器便可。

$ docker restart php7-fpm

啓動 nginx

以前已經成功啓動nginx了,只須要在這基礎上,增長 php 文件的解析就能夠了。
同理,先新建一個 nginx 配置文件: $ touch nginx/default.conf

其中,配置文件中須要將 php 文件轉發到 php-fpm 去處理便可,之前在宿主機中的配置通常都是轉發到 127.0.0.1:9000,可是如今容器的 ip 是不固定的,因此直接填 php7-fpm:9000,啓動容器時用 link 鏈接兩個容器,讓 docker 自動去識別容器的 ip 就能夠了。當中的 php7-fpm 爲 php-fpm 容器的名稱。 php 文件路徑要和 nginx 的文件路徑保持一致。

...
    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
    #    root           html;
        fastcgi_pass   php7-fpm:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /usr/share/nginx/html$fastcgi_script_name;
        include        fastcgi_params;
    }
...

啓動 nginx :

docker run -p 80:80 -v $PWD/project:/usr/share/nginx/html -v $PWD/nginx/default.conf:/etc/nginx/conf.d/default.conf --link php7-fpm:php7-fpm -d --name nginx-fpm nginx

此時,一個簡易的 lnmp 環境已經搭好了,查看一下容器啓動狀況:

leung@ubuntu:~/docker$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
490240532f1f        nginx               "nginx -g 'daemon ..."   About an hour ago   Up About an hour    0.0.0.0:80->80/tcp       nginx-fpm
d254489bd9e4        php:7.1-fpm         "docker-php-entryp..."   About an hour ago   Up 27 minutes       0.0.0.0:9000->9000/tcp   php7-fpm
7f184683cc8d        mysql               "docker-entrypoint..."   2 hours ago         Up 2 hours          0.0.0.0:3306->3306/tcp   mysql

測試訪問 php 文件

新建兩個測試文件: touch project/index.php project/i.php

index.php

<?php
//date
echo date("Y-m-d H:i:s")."<br />\n";

try {
    $conn = new PDO('mysql:host=mysql;port=3306;dbname=mysql;charset=utf8', 'root', '123456');
    // 這裏mysql 並不在同一個容器當中,因此鏈接數據庫時,只須要填寫 mysql 容器的名稱便可。
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}
$conn->exec('set names utf8');
$sql = "SELECT * FROM `user` WHERE 1";
$result = $conn->query($sql);
while($rows = $result->fetch(PDO::FETCH_ASSOC)) {
    echo $rows['Host'] . ' ' . $rows['User']."<br />\n";
}
?>

i.php

<?php phpinfo(); ?>

打開瀏覽器訪問 http://10.211.55.9/i.php:
圖片描述
圖片描述

Dockerfile

雖然咱們確實能快速搭建了一個 lnmp 的環境,可是每次切換宿主機時,都要執行多個步驟:

  1. 啓動 mysql 服務器
  2. 啓動 php 服務器
  3. 進入 php 容器修改配置
  4. 啓動 nginx 服務器

    怎麼說都仍是麻煩。若是能一鍵啓動的話,那就更好了。

Dockerfile 就能夠解決這樣的問題。

要使用 Dockerfile ,先要了解一下這究竟是個什麼東西。

什麼是 Dockerfile?

A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.

Dockerfile是一個文本文檔,其中包含用戶能夠在命令行上調用以組合鏡像的全部命令。使用docker構建用戶能夠建立一個自動構建,連續執行幾個命令行指令。

Dockerfile 經常使用命令

FROM

語法: FROM <image> [AS <name>] or FROM <image>[:<tag>][AS ] or FROM <image>[@<digest>][AS ]

注意:FROM 命令必須是 Dockerfile 的首個命令,但能夠在一個 Dockerfile 中出現屢次。

說明: 該命令定義了使用哪一個基礎鏡像啓動構建流程。基礎鏡像能夠爲任意鏡像。

MAINTAINER

語法: MAINTAINER <NAME>

說明: 設置生成鏡像的做者名字。

RUN

語法:

  • RUN <command> (shell形式,/bin/sh -c 或者是 Windows 的 cmd /S /C )
  • RUN ["<executable>", "<param1>", "<param2>"] (exec 形式)

說明:

RUN命令將在當前image中執行任意合法命令並提交執行結果。

RUN 指令緩存不會在下個命令執行時自動失效。

RUN命令用於建立鏡像(在以前commit的層之上造成新的層)。

ADD

語法:

  • ADD <src> [<src> ...] <dest>
  • ADD ["<src>", ... "<dest>"] (路徑中包含空格的必須使用該形式)

注意:ADD只有在build鏡像的時候運行一次,後面運行container的時候不會再從新加載了。

說明: 從 src 拷貝新的文件或文件夾或遠程文件 URL 到鏡像的文件系統的路徑 dest 中。

  • 全部拷貝到 container 中的文件和文件夾權限爲 0755 , uid 和 gid 爲0。
  • 若是文件是可識別的壓縮格式,則 docker 會自動解壓縮。
  • 若是 src 爲本地文件或文件夾,則必須相對於 docker build <PATH> 中的 <PATH> 目錄中。
  • dest 是一個絕對路徑,或者相對於 WORKDIR ,若是 <dest> 不存在則會自動建立。

COPY

  • COPY <src> [<src> ...] <dest>
  • COPY ["<src>", ... "<dest>"] (路徑中包含空格的必須使用該形式)

COPY 的語法和使用說明和 ADD 基本一致,可是在碰見壓縮包時, COPY 很直接的拷貝到容器當中,而不會解壓。 Docker 團隊建議大多數狀況下使用 COPY。

CMD

語法:

  • CMD ["<executable>","<param1>","<param2>"] exec 形式,比較好的形式。
  • CMD ["<param1>","<param2>"] 做爲 ENTRYPOINT 的默認參數。
  • CMD <command> <param1> <param2> shell 形式。

注意:Dockerfile 中只能有一個 CMD 指令,若是有多個則只有最後一個生效。

說明:

CMD 命令最主要的目的是提供一個默認的執行容器。

和RUN命令類似,CMD能夠用於執行特定的命令。和RUN不一樣的是,這些命令不是在鏡像構建的過程當中執行的,而是在用鏡像構建容器後被調用。

若是 CMD 用於爲 ENTRYPOINT 提供默認參數時, CMD 和 ENTRYPOINT 指令應該規定爲 JSON 數組格式。

docker run command 的命令匹配到 CMD command 時,會替換CMD執行的命令,即 docker run命令若是指定了參數會把CMD裏的參數覆蓋: (這裏說明一下,如:docker run -it ubuntu /bin/bash 命令的參數是指/bin/bash 而非 -it ,-it只是docker 的參數,而不是容器的參數,如下所說參數均如此。)

ENTERYPOINT

語法:

  • ENTRYPOINT ["<executable>", "<param1>", "<param2>"] (exec 形式,推薦)
  • ENTRYPOINT <command> <param1> <param2> (shell 形式)

注意:Dockerfile 中只能有一個 ENTRYPOINT 指令,若是有多個則只有最後一個生效。

說明:

配置容器啓動後執行的命令,而且參數不可被 docker run 提供的參數覆蓋。

若是結合 CMD 使用時,要使用 exec 的形式,即:

ENTRYPOINT ["echo"]
CMD ["HELLO_WORLD"]

這是 CMD 命令的內容則不是一個完整的指令,而是爲 ENTRYPOINT 命令提供默認參數,該參數能夠被 docker run 後的參數所覆蓋。

EXPOSE

語法: EXPOSE <port> [<port> ...]

說明: 通知 Docker 在運行時監聽指定的端口。主機上要用還得在啓動container時,作host-container的端口映射。

ENV

語法:

  • ENV <key> <value>
  • ENV <key>=<value> [<key>=<value> ...]

說明:

該指令將環境變量的 key 設置爲 value ,這些值均可以在 Dockerfile 後續的命令中用上,並能夠被修改。

VOLUME

語法:

  • VOLUME ["<path>", ...]
  • VOLUME <path> [<path> ...]

說明: 建立一個有具體名稱的掛載點,並將其標記爲從本機或者其餘容器外部的掛載卷。

USER

語法: USER <username | UID>

說明: 指定某個用戶運行容器。

WORKDIR

語法: WORKDIR </path/to/workdir>

說明: 設定指令 CMD , RUN , ENTRYPOINT , COPYADD 的工做目錄。能夠在一個 Dockerfile 中出現屢次,若是提供了一個相對路徑,那麼它將相對於前一個 WORKDIR 指令的路徑。

ARG

語法: ARG <name>[=default value>]

說明:

  • 定義一個變量,用戶能夠在創建的時候經過 docker build 命令使用 —build-arg <varname>=<value> 指定。
  • 多條 ARG 指令能夠定義指定的變量,可是在構建成功後取消。
  • 相同名字的環境變量會被 ENV 指令的覆蓋。
  • Docker 有一組預約義的 ARG 變量,能夠在 Dockerfile 中使用而不須要 ARG 指令。

ONBUILD

語法: ONBUILD <Dockerfile INSTRUCTION>

說明:

  • 當鏡像做爲另外一個鏡像構建的基礎時,添加一個被延時執行的觸發指令。就相似於在子鏡像的 Dockerfile 的 FROM 指令下插入了一條命令。
  • ONBUILD 指定的命令在構建鏡像時不執行,只在它的子鏡像中執行。
  • 任何構建指令均可以被註冊爲觸發器,但 ONBUILD 指令不必定觸發 FROM , MAINTAINER 或者 ONBUILD 指令。

Dockerfile 構建鏡像

如今將先前搭建 lnmp 的具體步驟一步步寫進 Dockerfile 中。

MySQL

FROM mysql

MAINTAINER LeungJZ

ENV MYSQL_ROOT_PASSWORD 123456

EXPOSE 3306

沒啥太多的配置,就是簡單的配置ROOT密碼和暴露3306端口。

構建鏡像:

leung@ubuntu:~/docker$ docker build ./mysql/ -t test-mysql
Sending build context to Docker daemon   2.56kB
Step 1/4 : FROM mysql
 ---> 141d24fea983
Step 2/4 : MAINTAINER LeungJZ
 ---> Using cache
 ---> aa6665261e02
Step 3/4 : ENV MYSQL_ROOT_PASSWORD 123456
 ---> Using cache
 ---> 4d482f48a09d
Step 4/4 : EXPOSE 3306
 ---> Using cache
 ---> 385bb10cd49a
Successfully built 385bb10cd49a
Successfully tagged test-mysql:latest

瞬間構建成功。再啓動一個容器 docker run --name mysql -p 3306:3306 -d test-mysql ,和之前基本一致。

Php7.1-fpm

FROM php:7.1-fpm

MAINTAINER LeungJZ

COPY php.ini /usr/local/etc/php/conf.d/php.ini

RUN /usr/local/bin/docker-php-ext-install pdo_mysql

EXPOSE 9000

CMD ["php-fpm"]

指定了 php-fpm 的版本爲 7.1,並覆蓋了咱們自定義的 php.ini ,而且安裝 pdo_mysql 拓展。

leung@ubuntu:~/docker$ docker build ./php/ -t test-php7
Sending build context to Docker daemon  70.14kB
Step 1/6 : FROM php:7.1-fpm
 ---> 9b44e8b4c8b6
Step 2/6 : MAINTAINER LeungJZ
 ---> Using cache
 ---> 1038ce686af9
Step 3/6 : COPY php.ini /usr/local/etc/php/conf.d/php.ini
 ---> Using cache
 ---> cdbdece75628
Step 4/6 : RUN /usr/local/bin/docker-php-ext-install pdo_mysql
 ---> Using cache
 ---> 8a8cbd9c8ac9
Step 5/6 : EXPOSE 9000
 ---> Using cache
 ---> 1fd3881b6769
Step 6/6 : CMD php-fpm
 ---> Using cache
 ---> 00d178a9351b
Successfully built 00d178a9351b
Successfully tagged test-php7:latest

由於以前構建過一次,因此全都是從緩存中獲取,直接就構建完成。

啓動: docker run --name php7-fpm --link mysql:mysql -p 9000:9000 -v $PWD/project:/usr/share/nginx/html -d test-php7

nginx

FROM nginx:1.13

MAINTAINER LeungJZ

COPY default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

ENTRYPOINT ["nginx", "-g", "daemon off;"]

配置就是基礎鏡像爲 1.13 版本的 nginx ,覆蓋自定義的 default.conf 配置文件,暴露80端口,而且不以守護進程模式啓動。

leung@ubuntu:~/docker$ docker build ./nginx/ -t test-nginx
Sending build context to Docker daemon  4.608kB
Step 1/5 : FROM nginx:1.13
 ---> da5939581ac8
Step 2/5 : MAINTAINER LeungJZ
 ---> Using cache
 ---> 3076338acc32
Step 3/5 : COPY default.conf /etc/nginx/conf.d/default.conf
 ---> Using cache
 ---> bb7ab780e5d4
Step 4/5 : EXPOSE 80
 ---> Using cache
 ---> 38dda9a94ae6
Step 5/5 : ENTRYPOINT nginx -g daemon off;
 ---> Using cache
 ---> 7864bf277ac0
Successfully built 7864bf277ac0
Successfully tagged test-nginx:latest

啓動: docker run --name nginx --link php7-fpm:php7-fpm -v $PWD/project:/usr/share/nginx/html -p 80:80 -d test-nginx

訪問 http://10.211.55.9/i.php 依舊能夠看到熟悉的 phpinfo 的界面。

小結

Dockerfile 能夠幫咱們減輕了不少配置方面的麻煩,可是啓動時,依舊須要綁定不少變量,如掛載卷,映射端口等。雖然只需 run 一次,可是這也是麻煩的。

固然,確定有解決的辦法,就是接下來的 docker-compose 。

docker-compose

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.

Docker-compose 是一個定義和運行多容器 Docker 程序的工具。在 compose 中,能夠用 YAML 文件來配置程序的服務,而後使用簡單的命令便可從配置中建立並啓動全部服務。

一個使用Docker容器的應用,一般由多個容器組成。使用Docker Compose,再也不須要使用 shell 腳原本啓動容器。在配置文件中,全部的容器經過 services 來定義,而後使用 docker-compose 腳原本啓動,中止和重啓應用,和應用中的服務以及全部依賴服務的容器。

安裝 compose

Linux:

# 下載最新的 docker-compose 
sudo curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

# 給予下載的 docker-compose 執行權限
sudo chmod +x /usr/local/bin/docker-compose

# 測試是否可用
docker-compose --version
docker-compose version 1.15.0, build e12f3b9

其餘操做系統能夠 查看這裏

配置 docker-compose.yml

整個配置文件應該包含 version 、services 、 networks 三大部分,其中最關鍵是 services 和 networks 兩部分。

version

下表顯示了 compose 文件版本支持特定的 docker 版本。

Compose file format Docker Engine release
3.3 17.06.0+
3.2 17.04.0+
3.1 1.13.1+
3.0 1.13.0+
2.3 17.06.0+
2.2 1.13.0+
2.1 1.12.0+
2.0 1.10.0+
1.0 1.9.1.+

services

在 services 標籤下的標籤是指該服務的名稱,爲用戶自定義。

  1. image

    指定服務的鏡像名稱或者鏡像ID ,若本地不存在,則 compose 將會嘗試拉取該鏡像。
    
    用法:
    image: ubuntu
    image: nginx:1.13
    image: containerID
  1. build

    服務便可基於某個鏡像 image ,亦可基於一個 Dockerfile 文件。在使用命令 `docker-compose up` 構建任務時,將會自動用 build 指向的 Dockerfile 路徑構建鏡像,並使用該鏡像啓動服務容器。
    
    **若是同時指定了 image 和 build,那麼 compose 會將 build 出來的鏡像命名爲 image。**
    
    用法:
    build: ./nginx
    # 設定上下文目錄
    build:
    context: ./
    Dockerfile: nginx/Dockerfile
  2. args

    和 Dockerfile 中的 ARG 用法同樣,在構建中指定環境變量,構建完成後取消。 args 中的變量能夠爲空值。
    
    **注意:如須要在 yaml 中使用布爾值(true,false, yes,no等)必需要用引號括起來。** 
    
    用法:
    args:
    - version=1.0
    - boolean='true'
    - emptyvar
  3. command

    能夠覆蓋啓動容器後默認執行的命令。
    
    用法:
    command: nginx
    command: [nginx, -g, deamon off]
  4. container_name

    設置容器的名稱,默認爲:<project><service><index>
    
    用法:
    container_name: lnmp_nginx
  5. depends_on

    設置依賴。有時候某個容器須要另一個容器做爲依賴,則該命令能夠解決依賴先後問題。如啓動 nginx 前要先啓動 php-fpm,這時候只須要這麼寫:
    depends_on:
    - php-fpm
  6. entrypoint

    用法和 Dockerfile 中的ENTRYPOINT 一致,會覆蓋 Dockerfile 中的 ENTRYPOINT。
  7. environment

    設置鏡像中的環境變量,能夠被保存在鏡像當中,即每次啓動容器,這些變量依舊存在容器當中。寫法和 args 一致:
    environment:
    - MYSQL_ROOT_PASSWORD: 123456
  8. expose

    暴露容器中的端口,和 Dockerfile 中的EXPOSE 用法一致。
  9. links

    相似於 `docker run` 中的 `—link` 參數,用於鏈接容器。如 nginx 容器須要鏈接 php-fpm 的容器,只須要這麼寫:
    links:
     - php7-fpm:php
其中,php 爲 nginx 容器中鏈接 php 的別名,php7-fpm 爲 php-fpm 服務的名字。
  1. ports

    用於映射端口,用法和 `docker run` 的參數 `-p` 一致:
    ports:
    - "8888:80"
將容器的 80 端口映射到宿主機的 8888 端口中。
  1. volumes

    掛載一個目錄或者一個數據卷容器到 container 中。數據卷的路徑能夠是絕對的,相對的。
    
     用法: `[HOST:CONTAINER]` or `[HOST:CONTAINER:rw]` rw的意思是,容器對該卷可讀可寫;ro爲只讀。
    volumes:
    # 只在容器中建立一個數據卷
    - /website/html
    # 絕對路徑
    -    /home/website:/website/html
    # 相對路徑
    -    ./data:/website/html
    # 已存在的數據卷容器,並設置權限爲只讀
    - data_container:/website/html:ro
  1. volumes_from

    從其餘容器或者服務掛載數據卷,可選參數爲 `:ro` 只讀 或者 `:rw` 可讀可寫。默認爲可讀可寫。
  2. networks

    指定容器加入的網絡:
    services:
      nginx:
        networks:
          web_app
web_app 爲網絡名稱。

還有其餘的配置項,具體的能夠去 官網查看

編寫一個多容器配置

先建立以下目錄:

./
├── conf            // 配置文件目錄
│   ├── mysql
│   ├── nginx
│   └── php
├── data            // mysql 數據目錄
├── logs            // 日誌文件目錄
│   ├── mysql
│   ├── nginx
│   └── php
└── www                // 項目目錄

新建一個 docker-compose.yml 文件:

version: '2'
services:
  nginx:
    image: nginx:1.13
    ports: 
      - "80:80"
    volumes:
      - ./www:/usr/share/nginx/html:rw
      - ./logs/nginx/:/var/log/nginx:rw
      - ./conf/nginx/conf.d:/etc/nginx/conf.d:ro
    links:
      - php:fpm
    depends_on:
      - php

  php:
    build: ./conf/php
    ports: 
      - "9000:9000"
    volumes:
      - ./www:/usr/share/nginx/html:rw
      - ./logs/php/:/var/log/php:rw
    links:
      - mysql:mysql
    depends_on:
      - mysql
  mysql:
    image: mysql:5
    ports: 
      - "3306:3306"
    volumes:
      - ./mysql:/var/lib/mysql:rw
    environment:
      MYSQL_ROOT_PASSWORD: "123456"
      MYSQL_USER: "test"
      MYSQL_PASSWORD: "testpass"

version 爲2 能夠兼容舊版本的docker。

services 這裏一共啓動了3個服務:

  • nginx。監聽 80 端口,掛載了 www 目錄爲工做目錄,掛載 logs/nginx 爲日誌目錄,掛載 conf/nginx/conf.d 至容器的 nginx 的配置文件夾。鏈接 php 容器,表示爲 fpm。依賴於 php 容器。
  • php。 監聽 9000 端口,掛載 www 目錄爲工做目錄,掛載 logs/php 爲日誌目錄,依賴於 mysql 容器並鏈接 mysql 容器。
  • mysql。監聽 3306 端口,掛載 mysql 目錄爲 mysql 數據存儲目錄,並設置了3個環境變量,分別爲 root密碼,mysql 用戶 test 並設置 test 的密碼爲 testpass。

配置文件自行添加,最終的目錄結構爲:

.
├── conf
│   ├── mysql
│   ├── nginx
│   │   └── conf.d
│   │       ├── default.conf
│   │       └── home.conf
│   └── php
│       ├── Dockerfile
│       └── php.ini
├── docker-compose.yml
├── logs
│   ├── mysql
│   ├── nginx
│   └── php
├── mysql
└── www
    ├── home
    │   └── index.html
    ├── index.html
    ├── index.php
    └── i.php

由於 php-fpm 咱們須要作額外的事情,因此使用 Dockerfile 來構建鏡像。php 的 Dockerfile 的內容配置和以前的 Dockerfile 一致:

FROM php:7.1-fpm

MAINTAINER LeungJZ

COPY php.ini /usr/local/etc/php/conf.d/php.ini

RUN docker-php-ext-install pdo_mysql

EXPOSE 9000

CMD ["php-fpm"]

能夠在 RUN 命令中,安裝須要的 php 拓展。

在項目根目錄中運行命令 docker-compose up 開始構建。

leung@ubuntu:~/compose$ docker-compose up
Pulling mysql (mysql:5)...
5: Pulling from library/mysql
aa18ad1a0d33: Already exists
fdb8d83dece3: Pull complete
75b6ce7b50d3: Pull complete
ed1d0a3a64e4: Pull complete
8eb36a82c85b: Pull complete
41be6f1a1c40: Pull complete
0e1b414eac71: Pull complete
914c28654a91: Pull complete
587693eb988c: Pull complete
b183c3585729: Pull complete
315e21657aa4: Pull complete
Digest: sha256:0dc3dacb751ef46a6647234abdec2d47400f0dfbe77ab490b02bffdae57846ed
Status: Downloaded newer image for mysql:5
Building php
Step 1/6 : FROM php:7.1-fpm
 ---> 9b44e8b4c8b6
Step 2/6 : MAINTAINER LeungJZ
 ---> Running in e5c103cc2d37
 ---> c396815cf8b6
Removing intermediate container e5c103cc2d37
Step 3/6 : COPY php.ini /usr/local/etc/php/conf.d/php.ini
 ---> bc4a19e69f93
Removing intermediate container 566b27944ffb
Step 4/6 : RUN /usr/local/bin/docker-php-ext-install pdo_mysql
 ---> Running in 8ebd9dadf506
  ---> a3483051c1bb
Removing intermediate container 8ebd9dadf506
Step 5/6 : EXPOSE 9000
 ---> Running in 9a4f81103c0c
 ---> d0376a2a8bfe
Removing intermediate container 9a4f81103c0c
Step 6/6 : CMD php-fpm
 ---> Running in 8d7f61c657f9
 ---> 77072c6b2561
Removing intermediate container 8d7f61c657f9
Successfully built 77072c6b2561
Successfully tagged compose_php:latest
WARNING: Image for service php was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating compose_mysql_1 ...
Creating compose_mysql_1 ... done
Creating compose_php_1 ...
Creating compose_php_1 ... done
Creating compose_nginx_1 ...
Creating compose_nginx_1 ... done
Attaching to compose_mysql_1, compose_php_1, compose_nginx_1

不出1分鐘,整個項目環境就已經搭建好了。根據提醒, php 服務已經被構建出來了,由於它一開始並不存在。若是想要從新構建整個服務,須要運行 docker-compose build or docker-compose up --build

這時 docker ps 查看:

leung@ubuntu:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                    NAMES
18bc61bd12b1        nginx:1.13          "nginx -g 'daemon ..."   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp       compose_nginx_1
38ccb807e09b        compose_php         "docker-php-entryp..."   About a minute ago   Up About a minute   0.0.0.0:9000->9000/tcp   compose_php_1
67188d651b80        mysql:5             "docker-entrypoint..."   About a minute ago   Up About a minute   0.0.0.0:3306->3306/tcp   compose_mysql_1

能夠看到已經成功啓動了三個容器。

經過瀏覽器訪問:
圖片描述

能夠發現已經運行成功,並且 php 文件也能正常執行。

docker-compose 命令

docker 差很少,也有 start , stop, build , logs , rm 等經常使用命令,其做用也是相似的。

總結

docker 真是一個神器!!!

這只是一個簡單的入門,但願你們能繼續挖掘 docker 更強大的功能。

相關文章
相關標籤/搜索