Dockerfile 与 Compose 环境搭建学习笔记(二)

上一篇文章对整体结构进行了简单记录,这一篇介绍下关于Dockerfile自定义镜像以及各个服务的配置。

其实 https://hub.docker.com/ 上面各种基础镜像非常完善,特别是官方的镜像质量非常之高,而我再捣腾一次完全是为了让自己掌握 Dockerfile 方面的技能而已。

在选择基础镜像方面,推荐使用 Alpine ,然后再它上面进行定制,因为它非常的小仅3M。我的 Nginx/Redis 是在 Alpine 基础上定制的,PHP 是在 CentOS7上面进行的定制。截图大家可以感受下大小:

Dockerfile 与 Compose 建立关联

关于概念可以看这里:
https://yeasy.gitbooks.io/docker_practice/content/image/build.html

我这里以 PHP/Redis/Nginx 的定制来进行一些说明(我也只是现学现用,希望高手多指教)。

在上篇的 docker-compose.yml 文件中如下的配置:

dev.nginx.srv:
    image: lei_nginx:1.14.0
    build: ./nginx
    volumes:
        - ./nginx/conf:/home/work/app/nginx/conf
      - ./www:/home/work/www
    ports:
        - "80:8080"
      - "443:443"
    restart: always

这里重要的是多了 build 这个选项,设置的对应目录中可以找到 Dockerfile 这个文件,当我们 docker-compose up 时,docker会根据这个文件去先创建镜像,然后启动一个容器。

Dockerfile 如何写

网络上有非常多关于 Dockerfile 该如何写的最佳实践,我觉得有几点特别重要:
- 一个容器只运行一个进程;
- 镜像层数尽可能少,当然还需要考虑可读性等方面的因素;
- RUN指令应该用 \ 分成多行方便阅读;
- 容器镜像要尽可能的小。

更多最佳实践可以看这里:
https://yeasy.gitbooks.io/docker_practice/content/appendix/best_practices.html

接下来以 Redis 的 Dockerfile 来聊一聊实际如何编写。

FROM alpine:3.7

# 解释信息
LABEL maintainer="HeLei <dayugog@gmail.com>"

ENV REDIS_VERSION=3.2.11 \
    SRC_DIR=/home/work/src \
    DATA_DIR=/home/work/app/redis/data \
    CONF_DIR=/home/work/app/redis/conf

# 设置系统时区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

COPY src/ $SRC_DIR

# 编译文件
RUN set -ex; \
    \
    addgroup -S work && adduser -S -G work work; \
    apk add --no-cache --virtual .build-deps \
            coreutils \
            gcc \
            jemalloc-dev \
            linux-headers \
            make \
            musl-dev \
        ; \
    \
    cd $SRC_DIR; \
    tar xvzf redis-$REDIS_VERSION.tar.gz; \
    cd redis-$REDIS_VERSION; \
    make && make install; \
    apk del .build-deps; \
    \
    mkdir -p $DATA_DIR && mkdir -p $CONF_DIR; \
    chown -R work:work /home; \
    rm -rf $SRC_DIR

# 拷贝配置文件
COPY conf/ /home/work/app/redis/conf

COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

# 导出端口
EXPOSE 6379
# 启动redis
CMD ["redis-server"]

第一行 FROM 用来指定基础镜像。也就是你要在什么镜像上进行定制,我这里选择的是 alpine,这是一个提供的基础空白对象非常小。只是它上面的包管理是 apk ,使用时需要掌握下它的一些参数。

LABEL可以理解成添加一些说明、描述信息。我这里仅添加了自己的联系方式。可以通过反斜线 \ 来进行换行。

ENV用来设置环境变量,例如:定义一些系统版本、路径的环境变量,在后续RUN中可以使用(当然不仅仅是RUN中可用),也可以用改写原有的环境变量,例如:PATH。

RUN这是一个非常重要的命令,它是用来执行命令行的命令。就像上面看到的用 yum 安装更新软件,make编译代码等。可以通过反斜线 \ 来进行换行。

COPY它是将宿主机的内容复制到容器中指定的路径。

EXPOSE指令用于指定容器将要监听的端口。一般设置为应用程序使用常见的端口,例如Redis设置为:6379

现在重点说下 CMDENTRYPOINT 两个命令。如果Dockerfile中没有 ENTRYPOINT 选项,CMD 的内容就相当于直接执行某个命令。但是当存在时就是另外一回事。以上面的为例:

COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

# 启动redis
CMD ["redis-server"]

这里设置了一个 ENTRYPOINT ,像上面这种情况的时候如果直接启动一个容器时,相当于最后应用启动执行的命令是:./docker-entrypoint.sh redis-server

根据这个特性,docker-entrypoint.sh 内部可以根据相关参数进行特殊处理。来看下我的 docker-entrypoint.sh 脚本内容

#!/bin/sh
set -e

cd `dirname $0`

# 对文件夹进行权限修改
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
    chown -R work:work /home
  exec redis-server /home/work/app/redis/conf/redis.conf
fi

exec "$@"

可以看到如果脚本后面带的参数是redis-server则会先进行相关目录授权,然后启动redis。如果不是就会直接执行,例如:

➜ ~/dockerEnv >docker run -it --rm redis:3.2.11 redis-cli -v
redis-cli 3.2.11

会直接执行后面这个命令,你可以看到redis客户端的版本信息。这也就是表示,可以把镜像当成一个命令来使用了。

有了 ENTRYPOINT 这个功能,可以用它在服务启动时,做更多操作 。例如可以结合 docker-compose.yml 中设置的环境变量做更多事情。可以查看官方的MySQL的 docker-entrypoint.sh 文件内容。

依据Dockerfile启动容器

Dockerfile 已经写好了,通过下面的命令即可创建镜像启动容器。

➜ ~/dockerEnv >docker build -t lei_redis:3.2.11 .

在 redis/ 目录下执行上面的命令,他会先获取基础镜像,然后根据命令逐条执行,完成redis的编译、安装以及相关清理工作。

编译完成后可用通过docker image ls查看当前的镜像列表数据。

然后通过 docker run -it -p 6379:6379 -d lei_redis:3.2.11 启动一个容器。

启动完成后,大家可以用redis客户端链接查看redis已经正常启动。

当然还有 PHP/Nginx 的镜像定制,以及每个服务的配置,大家可以在github上查看详情,这里就不再赘述了,剩下再介绍下这个过程中遇的到的几个错误。

遇到的错误

  1. 在宿主机中无法连接Redis
    这是由于bind的问题。以前在 vagrant 中安装redis也遇到过, 通过将配置修改为:
bind 0.0.0.0

宿主机能够连接到服务器上。这样设置的含义是,让容器中的Redis监听容器ip的所有端口。这样设置而不是指定ip是因为每个镜像可以启动多个容器,而每个容器的ip地址是不确定的。

  1. 镜像创建时报错
    报错信息如下:
ERROR: for dockerenv_dev.php-fpm.srv_1  Cannot start service dev.php-fpm.srv: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"docker-entrypoint.sh\": executable file not found in $PATH": unknown

这个问题主要是:我的 docker-entrypoint.sh 文件没有可执行权限,因此在镜像创建完后,执行ENTRYPOINT指定的脚本时导致错误,解决办法当然很简单,直接执行:chmod +x docker-entrypoint.sh。然后需要重新创建镜像。

  1. Nginx 无法连接php-fpm
    这个错误其实与宿主机无法连接Redis很像,错误信息:
2018/06/13 11:13:26 [error] 5#0: *8 connect() failed (111: Connection refused) while connecting to upstream, client: 172.18.0.1, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://172.18.0.2:9000", host: "localhost"

修改 php-fpm 的监听地址为:0.0.0.0:9000,Nginx可正常启动。

  1. 访问php文件时找不到文件
    执行动态文件时,出现了文件找不到的提示,具体错误信息:
2018/06/13 11:21:20 [error] 5#0: *10 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: 172.18.0.1, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://172.18.0.2:9000", host: "localhost"

由于Nginx与PHP没有部署在同一个容器中,相关的项目文件只与Nginx进行了共享,而没有与PHP的容器进行共享。因此当访问静态文件时,Nginx直接在自己的容器中完成操作,而访问php文件时信息传到了PHP所在的容器,容器内部无法找到对应的php文件而导致的错误。

总结

经过2天的折腾,算是基本把环境搭建起来了。不过还有一些其他问题需要思考该如何进行:

  • 如果我的PHP需要新的扩展,该如何去编译这个扩展包?
  • 如何去监控docker中的应用的状态?比如:Redis/Nginx等服务的状态。

后续会继续摸索分享自己的经验。

项目地址:
https://github.com/helei112g/docker-env

微信公众号:dayuTalk
pub

参考资料:

发布了113 篇原创文章 · 获赞 154 · 访问量 62万+
展开阅读全文

在docker和ubuntu中从上游读取响应头时失败(104:由对等方重置连接)

12-03

<div class="post-text" itemprop="text"> <p>I created a <code>DockerFile</code> like below</p> <pre><code>FROM ubuntu:18.04 MAINTAINER Amin Keshavarz <ak_1596@yahoo.com> # Add your github access token if needed in composer update as arg or env var. ARG github_access_token ENV github_access_token=${github_access_token} ENV DEBIAN_FRONTEND=noninteractive # Install dependency packages RUN apt-get update && apt-get install -yq --no-install-recommends \ git \ curl \ ca-certificates \ # php \ php7.2-fpm php7.2-common \ php7.2-mongodb php-pear php7.2-dev RUN apt-get install -y build-essential # Install mongodb driver RUN pecl install mongodb #RUN echo "extension=mongodb.so" >> /etc/php/7.2/fpm/php.ini #RUN echo "extension=mongodb.so" >> /etc/php/7.2/cli/php.ini RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer RUN apt-get install -y php7.2-mbstring \ php7.2-intl \ php7.2-soap \ php7.2-curl \ php7.2-imap \ php7.2-zmq \ php7.2-bcmath \ php7.2-gd \ php7.2-zip # Add working directory and copy files into that. RUN mkdir /app VOLUME /app WORKDIR /app COPY . /app # Start application installation by composer update command. #RUN composer config -g github-oauth.github.com $github_access_token RUN composer global require fxp/composer-asset-plugin #RUN composer update -vvv ENTRYPOINT service php7.2-fpm start && /bin/bash CMD ["php-fpm"] EXPOSE 9000 </code></pre> <p>And using below <code>docke-compose.yml</code></p> <pre><code>version: "3" services: web: build: context: . dockerfile: ./docker/Dockerfile container_name: "crm_web" tty: true ports: - "9000:9000" networks: - default volumes: - .:/app nginx: image: nginx:1.10.3 container_name: "crm_nginx" ports: - 8080:80 restart: always volumes: - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf - .:/app links: - web depends_on: - web </code></pre> <p>And has below nginx.conf</p> <pre><code>server { client_max_body_size 100M; set $host_path "/app"; access_log /app/log/access.log main; server_name _ localhost; root $host_path/; set $yii_bootstrap "index.php"; charset utf-8; location / { index index.html $yii_bootstrap; try_files $uri $uri/ /$yii_bootstrap?$args; } location ~ ^/(protected|framework|themes/\w+/views) { deny all; } #avoid processing of calls to unexisting static files by yii location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { try_files $uri =404; } # pass the PHP scripts to FastCGI server listening on web:9000 # location ~ \.php { fastcgi_split_path_info ^(.+\.php)(.*)$; #let yii catch the calls to unexising PHP files set $fsn /$yii_bootstrap; if (-f $document_root$fastcgi_script_name){ set $fsn $fastcgi_script_name; } fastcgi_pass web:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fsn; #PATH_INFO and PATH_TRANSLATED can be omitted, but RFC 3875 specifies them for CGI fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fsn; } # prevent nginx from serving dotfiles (.htaccess, .svn, .git, etc.) location ~ /\. { deny all; access_log off; log_not_found off; } } </code></pre> <p>But when i try to connect my host <code>http://localhost:8080</code> i get below error in my console from docker:</p> <blockquote> <p>crm_nginx | 2018/12/03 14:48:14 [error] 28#28: *17 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 172.18.0.1, server: _, request: "GET /web/ HTTP/1.1", upstream: "fastcgi://172.18.0.2:9000", host: "localhost:8080"</p> </blockquote> <p>And get <code>502 Bad Gateway</code> in browser.</p> <p>Can you help me solve this problem? What i missed?</p> </div> 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 猿与汪的秘密 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览