前言

大体分三部分:

  • 镜像与容器运行、进入、互通与网络
  • Dockerfile
  • docker-compose

基本是我的学习Docker的一个历程,从最麻烦(逐条命令输入)到看起来相应比较简便的方式(docker-compose.yml文件)构建一个LNMP环境。
因为也是学习没多长时间,尽量会在能力范围内对文章内的知识点进行描述和归纳,当然会有对应的官方文档。
除官方文档外的信息建议先自己思考下,本文仅个人的拙见,欢迎评论指正。

镜像与容器运行

本节主要说下如何拉取镜像与简单地运行一个容器

镜像(image)与容器(container):
镜像是一堆只读层,无法更改,容器是在镜像上面加了可读可写层。
镜像从仓库拉取,仓库有分共有和私有,我们暂时只谈公有的官方仓库DockerHub

首先拉取的命令是

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

比如我拉取一个nginx镜像,可以先到DockerHub搜索nginx
点击TAGS能看到所有标签,假如我们选择的是latest也就是最新的

[[email protected] ~]# docker pull nginx:latest
[[email protected] ~]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              5a3221f0137b        1 hours ago         126MB

这是我们就已经成功拉取一个镜像到本地,并通过docker image ls命令查看了该镜像的信息
ps:使用docker pull命令拉取镜像时默认是指向DockerHub的。

接下来使用docker run命令运行一个nginx容器

[[email protected] ~]# docker run -d nginx:latest
ee67205b7325a2612ff56433aeef2c5ece248cd2ee0e1fae194524fb6d260568
[[email protected] ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                      NAMES
ee67205b7325        nginx:latest        "nginx -g 'daemon of…"   7 seconds ago       Up 6 seconds        80/tcp                                     festive_solomon

docker run命令里-d参数的作用是后台运行容器。
docker ps -a命令用来查看所有容器情况,看STATUS是Up的就行。
顺带提一下PORTS是80/tcp并不是代理了本机的80端口,是代理了容器的80端口。
可以理解为每个容器都是一个虚拟机,有独立的可读写文件目录,可分配独立的IP/端口等等。

运行容器的一些常用参数

本节说下在运行容器时一般会用到的参数,如转发端口、挂载卷、网络、容器名等等

先列出我目前会用到的docker run参数,更多参数和解释看官方文档
-d 以后台方式启动,无需传值
--name 指定容器名,值是容器名
-p 设置宿主机和容器的端口转发,参数值格式是 [宿主机端口号]:[容器端口号]
--network 指定容器假如的网络,值是网络名
--ip 指定在该网络内的IPv4地址
-v 设置挂载卷,值格式是 [宿主机绝对路径]:[容器绝对路径] ;文件夹没有会创建,有的话会被宿主机的文件夹替代,不会进行覆盖、共存的情况。
--user 指定运行时的用户,值格式是 [宿主机用户名]:[宿主机用户群组]
-e 向容器传入环境变量,值格式是 [键]=[值]
--rm 容器仅运行一次,如果使用docker stop命令后容器将被清除

运行容器实例

沿用上面的nginx:latest镜像,先将上一步运行的容器停止并删除,然后再提供一个相对完整的实例。
(当然具体还是要根据需求来调整,不可能大家都一样的,如果有更好的建议也可评论留言讨论下)
由于上一步我们没有使用--name指定容器名,所以我们使用停止、删除容器命令时,定位容器用的是容器ID ee67205b7325 。(通过上一步 docker ps -a 命令查看到)

[[email protected] ~]# docker stop ee67205b7325
ee67205b7325
[[email protected] ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS                                      NAMES
ee67205b7325        nginx:latest        "nginx -g 'daemon of…"   18 hours ago        Exited (0) 2 seconds ago                                              festive_solomon
[[email protected] ~]# docker rm ee67205b7325
ee67205b7325

首先我们使用了docker stop 命令,指定ID为ee67205b7325的这个容器将其停止运行。
第二步通过 docker ps -a 查看容器状态,STATUS是Exited的
最后用 docker rm 命令,指定ID为ee67205b7325的容器将其删除,其实最后如果再次通过 docker ps -a 查看的话,容器就会不复存在了。
联动上一节的 --rm 参数:如果运行容器时加上了该参数,在第一步停止运行容器后,容器会被清除,省掉 docker rm 步骤。

进入正题:
相信从这里开始算是入门了。故不做过多描述,直接贴指令,简单解释下比较难理解、容易混淆的地方和一些坑。

首先在宿主机的/var/www/目录新建html和nginx这两个文件夹。(目录只是为了方便)
html文件夹放网页文件,nginx文件夹是放nginx配置,然后在html文件夹里面写一个hello world的index.html。

运行容器

[[email protected] ~]# docker run -d \
> --name nginx-t \
> -p 80:80 \
> -p 443:443 \
> -v /var/www/html:/var/www/html \
> -v /var/www/nginx:/etc/nginx/conf.d \
> nginx:latest
38da0f7528e801b2fce88e5774871c203709a8152796e6c7b854f7a02fd27add
[[email protected]_0_13_centos nginx]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                      NAMES
38da0f7528e8        nginx               "nginx -g 'daemon of…"   15 seconds ago      Up 14 seconds       0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   nginx-t

基本用的参数上一节都有解释。
docker ps -a 结果,跟之前运行多了两个东西,一个是NAME,另一个是PORTS
说下PORTS,字面意思就是端口代理,这里的 0.0.0.0:80->80/tcp0.0.0.0:443->443/tcp 宿主80转发至容器80端口,协议是tcp,443一样。
现在直接在浏览器输入你宿主机的IP会得到一个NGINX的欢迎页,后续去宿主机的/var/www/nginx里添加一个xxx.conf配置文件,里面简单写写就能看到你的hello world页面。

server{
    listen 80;
    server_name 你的域名;
    location / {
        root /var/www/html;
        index index.html index.htm;
    }   
}

查看错误日志

在启动容器的时候往往会遇到各种各样的错误,但是在使用 docker ps -a 查看容器状态时又没有错误信息提示,只有个Exit的状态表示。
这时我们可以通过查看日志的方式来定位错误:

docker logs [OPTIONS] CONTAINER

OPTIONS选项有:
--details 显示详情
--follow , -f 实时更新
--since Show logs since timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
--tail all 要显示多少行(从最后一行往前数起)
--timestamps , -t 显示时间
--until 指定在一个时间点之前的日志 格式可以是(yyyy-mm-dd)和相对的时间值(42m for 42 minutes)

那假如我们看容器 mysql-t 最后50行的日志,并让其实时更新:

docker logs -f -t --tail 50 mysql-t

进入容器

假设我运行这个nginx容器后需要对/etc/nginx/nginx.conf配置文件进行修改,比如是运行的用户。
这时候需要使用 docker exec 命令进入容器进行操作。 官方文档

[[email protected] ~]# docker exec -it nginx-t bash
[email protected]:/#

这时候你就进到了nginx-t这个容器里面,相当于进入到一台虚拟机里面,从第二行能看到命令行左边的登录信息会有所变更。
要退出回到宿主机的话使用 exit 命令即可。
exec命令在我使用逐条docker命令装lnmp环境时,在php容器里用得最多,毕竟启动后还要进去装扩展、启用什么的。

创建Docker的网络

假如我分别运行了nginx和php容器,这两个容器运行时若没有使用--network参数,会指定假如一个Docker默认的桥接(bridge)网络。
这个桥接网络的IP段有可能是安装Docker时随机分配,需要使用docker inspect命令查看IP段、网关是多少,容器互相通讯时非常不方便。
那我们可以自己创建一个网络,自定义模式(桥接),IP段,名称等等....

[[email protected] ~]# docker network create --driver=bridge --subnet=172.18.0.0/24 net-t
ea3bfa94337fba53e4874dfb3a6ada9e646ed7e4c4b19e654e4a3eebc386643e

上面命令是创建了一个名为net-t桥接网络
IP段为172.18.0.x
/24 == 子网掩码 == 255.255.255.0
所以可用IP范围为 172.18.0.1 ~ 172.18.0.254

查看已有网络可以通过 docker network ls 命令
删除已有网络可以通过 docker network rm [NAME] 命令

容器之间的互相通讯

上上节说如果没有创建一个自定义的Docker网络的话,不同容器之间通讯会比较麻烦。
举个例子:
运行了nginx和php容器均加入默认的桥接网络,nginx配置需要反代至php容器的9000端口,此时你完全不知道Docker默认桥接网络的IP段是多少。
1.这时需要先通过 docker inspect [php_container_name] 命令查看php容器的具体IP是多少,再写到nginx的xxx.conf里;
2.通过 docker inspect [network_name] 命令查看默认桥接网络的网关是多少,比如是172.18.0.1,nginx的xxx.conf里就直接写172.18.0.1:9000。
后者看起来相对比较简单,但这依然不利于后面的Dockerfile、 docker-compose使用。
这时需要先通过上一步的创建Docker网络,指定IP段,然后在容器运行的时候通过--network参数声明加入该网络,并通过 --ip 参数分配一个ipv4地址。

直接上实例:
我们刚才已经创建了一个名为net-t的网络,网段为172.18.0.x
接下来我们将重新运行nginx和新运行一个php容器,nginx容器的ip为172.18.0.2,php容器ip为172.18.0.3

先将之前的nginx容器删掉重新运行

[[email protected] ~]# docker stop nginx-t
nginx-t
[[email protected] ~]# docker rm nginx-t
nginx-t
[[email protected] ~]# docker run -d \
> --name nginx-t \
> -p 80:80 \
> --ip=172.18.0.2 \
> --network=net-t \
> -v /var/www/html:/var/www/html \
> -v /var/www/nginx:/etc/nginx/conf.d \
> nginx:latest
38da0f7528e801b2fce88e5774871c203709a8152796e6c7b854f7a02fd27add

接下来运行php容器

[[email protected] ~]# docker run -d \
> --network=net-t \
> --ip=172.18.0.3 \
> -p 9000:9000 \
> --name php-t \
> -v /var/www/html:/var/www/html \
> php:7.1-fpm 
3e289cf2175a8cf0fddeec41bf5d975730adc71f87df56cd3d987b0fb3c8231c

记得去编辑下/var/www/nginx/xxx.conf

server{
    listen 80;
    server_name 你的域名;
    location / {
        root /var/www/html;
        index index.php index.html index.htm;
    }

    location ~\.php(.*)$ {
        root /var/www/html/test;
        fastcgi_pass 172.18.0.2:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
        include fastcgi_params;
    }
}

为了顺便测试php,可以去/var/www/html把里面的index.html换成index.php,内容改成:

<?php 
    phpinfo();

此时访问你的IP或者域名如果看到PHP信息页代表容器互通没问题,PHP容器也顺利运行。

Mysql容器运行

[[email protected] ~]# mkdir /var/www/mysql/data
[[email protected] ~]# useradd -g mysql:mysql
[[email protected] ~]# chown mysql:mysql /var/www/mysql/data
[[email protected] ~]# docker run \
> -p 3306:3306 \
> --network=net-t \
> --ip=172.18.0.4 \
> --name mysql-t \
> --user mysql:mysql \
> -e MYSQL_ROOT_PASSWORD=123456 \
> -v /var/www/mysql/conf:/etc/mysql/conf.d \
> -v /var/www/mysql/data:/var/lib/mysql \
> -d \
> mysql:5.7
c84f1d051faa9ea885b00a09dc7c0b47c82630828126664b1704e479c7fbf948

需要注意几点:

  1. 创建一个存放mysql数据的文件夹,并设置一个用户(mysql)作为拥有者;
  2. 容器运行时通过--name参数指定容器使用该用户运行,否则会报无法写入文件的错误。

这样做是为了把数据保存在宿主机而非容器保证数据的安全,不然哪天手滑docker rm就GG了。

-e 传了一个键为MYSQL_ROOT_PASSWORD的环境变量给容器,这样在容器运行时Mysql默认会取该变量作为root用户的密码。

当你打算在/var/www/html放你的php项目,php在连接Mysql时,相信会出现没有安装扩展的报错,或者在这之前已经有其他扩展的报错了。
这个问题我们会在接下来的Dockerfile篇有讲到如何安装php扩展。

标签: none

添加新评论