适用于Symfony 4的Docker-从LAN到生产

史前史


有一天,我需要为我的项目部署开发环境。 Vagrant已经受够了,希望为所有项目参与者提供一个与生产服务器相同的开发环境。 因此,在听了有关时髦泊坞窗的信息之后,我决定开始处理它。 接下来,我将尝试尽可能详细地描述从在LAN上安装docker到将产品部署到KVM的所有步骤。

原始技术栈:

-码头工人
-symfony 4
-Nginx
-php-fpm
-PostgreSQL的
-elasticsearch
-Rabbitmq
-詹金斯

铁:

-操作系统Ubuntu 16.04下的笔记本电脑
-KVM托管上的生产服务器

为什么除了技术堆栈之外,我还列出了铁堆栈?

如果您以前从未使用过Docker,则可能会遇到许多与硬件,笔记本电脑的操作系统或托管虚拟化类型有关的问题。

开始使用Docker时,第一个也是最重要的方面是笔记本电脑的操作系统。 使用docker的最简单方法是在linux系统上。 如果您在Windows或Mac上工作,那么您将遇到100%的困难,但是这些困难并不重要,并且如果您想“谷歌”如何解决此问题,将没有任何问题。

第二个问题是托管。 为什么KVM虚拟化类型需要托管? 原因是VPS虚拟化与KVM完全不同,并且您根本无法在VPS上安装docker,因为VPS动态分配服务器资源。

小计:要在Docker上最快启动,最好选择Ubuntu作为本地OS和KVM托管(或您自己的服务器)。 此外,故事将精确地依赖于这两个组成部分。

Docker-组成局域网


安装方式


首先,您需要在本地安装docker本身。 您可以在官方网站上查看到ubuntu官方文档链接的安装说明(您需要安装docker和docker-compose),或者通过在控制台中运行以下命令:

curl -sSl https://get.docker.com/ | sh 

该命令将同时安装docker和docker-compose。 之后,使用以下命令检查docker版本:

 docker --version 

我将从Docker 18.06.0-ce版本开始整个工作。

安装完成!

意识


要处理不太成功的内容-您需要了解其工作方式。 如果您以前只使用Vagrant或类似工具,那么一开始这将是非常不寻常且难以理解的,但这只是一开始。

我将尝试为Vagrant打个比喻。 现在许多人可以说,比较Vagrant和Docker根本是错误的。 是的,我同意这一点,但是我不打算将它们进行比较,我只会尝试向仅与Vagrant合作Docker工作系统的新人传达信息,以吸引新人了解。

我对“手指上的”容器的看法如下:每个容器都是一个孤立的小世界。 可以将每个容器想象成一个很小的Vagrant,上面只安装了1个工具,例如nginx或php。 最初,容器通常与周围的一切都隔离开来,但是通过棘手的操作,您可以配置所有内容,以便它们彼此通信并一起工作。 这并不意味着每个容器都不是一个单独的虚拟机。 但是,对我来说,初步了解起来比较容易。

Vagrant只是消耗掉部分计算机资源,创建虚拟机,在其上安装操作系统,安装库,安装您在流浪汉之后在脚本中编写的所有内容。 最终,它看起来像这样:

查看示意图

反过来,Docker的工作原理则截然不同。 它不会创建虚拟机。 Docker使用其Alpine操作系统和该应用程序正常运行所需的1-3个库(例如php或nginx)来创建容器(目前,您可以将它们视为微虚拟机)。 同时,Docker不会自己阻止系统资源,而仅在必要时使用它们。 最终,为了说明,它看起来像这样:

查看示意图

每个容器都有一个从中创建映像。 绝大多数映像是另一个映像的扩展,例如,Ubuntu xenial或Alpine或Debian,在上面附加了驱动程序和其他组件。

我的第一张图片是用于php-fpm的。 我的图像扩展了官方的php图像:7.2-fpm-alpine3.6。 也就是说,从本质上讲,它使用正式图像并提供我需要的组件,例如pdo_pgsql,imagick,zip等。 因此,您可以创建所需的图像。 如果需要,可以在此处使用它。

通过图像的创建,在我看来,如果它们例如是基于xenial制作的,则一切都非常简单,但是如果它们是基于Alpine制作的,则它们会散发出一点痔疮。 在开始使用docker之前,我基本上没有听说过Alpine,因为Vagrant始终在Ubuntu xenial下为我工作。 Alpine是一个空的Linux操作系统,其中根本没有任何东西(最低要求)。 因此,起初使用它非常不方便,因为例如没有相同的apt-get安装(您已经习惯了),但是只有apk add和一套不太合理的软件包。 例如,如果Xenial(抽象)重500袋,那么Alpine就是其最大的优势,而Alpine(抽象)重约78袋。 这甚至会影响什么? 这会影响最终将存储在您的服务器上的所有映像的生成速度和最终权重。 假设您有5个不同的容器,并且所有基于xenial的容器的总重量将超过2.5个演出和阿尔卑斯山-仅约500袋。 因此,理想情况下,我们应努力确保容器尽可能的薄。 (在Alpine中安装软件包的有用链接-Alpine软件包 )。

他们docker hub上的每个地方写了如何使用docker run启动容器,由于某种原因,他们没有写出如何通过docker-compose来启动容器,而通过docker-compose,它在大多数情况下都会启动,因为很少有狩猎手动启动所有容器,网络,开放端口等。 代表用户的Docker-compose看起来就像是带有设置的yaml文件。 它包括对必须启动的每个服务的描述。 我针对本地环境的构建如下:

 version: '3.1' services: php-fpm: image: otezvikentiy/php7.2-fpm:0.0.11 ports: - '9000:9000' volumes: - ../:/app working_dir: /app container_name: 'php-fpm' nginx: image: nginx:1.15.0 container_name: 'nginx' working_dir: /app ports: - '7777:80' volumes: - ../:/app - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf postgres: image: postgres:9.6 ports: - '5432:5432' container_name: 'postgresql' working_dir: /app restart: always environment: POSTGRES_DB: 'db_name' POSTGRES_USER: 'db_user' POSTGRES_PASSWORD: 'db_pass' volumes: - ./data/dump:/app/dump - ./data/postgresql:/var/lib/postgresql/data rabbitmq: image: rabbitmq:3.7.5-management working_dir: /app hostname: rabbit-mq container_name: 'rabbit-mq' ports: - '15672:15672' - '5672:5672' environment: RABBITMQ_DEFAULT_USER: user RABBITMQ_DEFAULT_PASS: password RABBITMQ_DEFAULT_VHOST: my_vhost elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:6.3.0 container_name: 'elastic-search' environment: - discovery.type=single-node - "discovery.zen.ping.unicast.hosts=elasticsearch" - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ports: - 9200:9200 - 9300:9300 working_dir: /app volumes: - ../:/app - ./data/elasticsearch:/usr/share/elasticsearch/data volumes: elasticsearch: postgresql: 

SF4的docker-compose.yaml是一组特定的服务:nginx,php-fpm,postgresql,rabbitmq(如果需要),elasticsearch(如果需要)。 对于本地环境,这足够了。 要使其全部正常工作-仅有极少数设置,没有这些设置将无法工作。 最常见的是映像,卷,端口,环境,working_dir和container_name。 在hub.docker.com上的文档中描述了用于启动该映像或该映像的所有内容 。 并非总是有关于docker-compose的描述,但这并不意味着它无法使用。 仅需要将所有传入数据从docker run命令传输到docker-compose,一切都会正常进行。

例如,这里有RabbitMQ的图像。 当您第一次看到此内容时,它会引起混杂的感觉和情绪,但并非所有内容都如此可怕。 标签在此图像中指示。 通常,标记-代表不同的图像,具有不同可扩展图像的不同版本的应用程序。 例如,标签3.7.7-alpine表示此图像比例如3.7.7薄,因为它基于Alpine。 很好,而且在标签中,最经常指出应用程序的版本。 我通常选择应用程序本身和高山映像的最新版本和稳定版本。

学习并选择标签后,您通常会看到以下内容:

 docker run -d --hostname my-rabbit --name some-rabbit -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password rabbitmq:3-management 

第一个想到的是WTF? 如何将其转移到docker-compose?

一切都很容易。 实际上,此行指示与yaml文件中所有相同的参数,仅缩写。 例如,-e是在其中传递各种参数的环境,也可能有-p之类的条目-这些端口在yaml中称为端口。 因此,为了以高质量的方式使用不熟悉的图像,您只需要“ google” docker run缩写并在yaml文件中应用全名。

现在回到docker-compose.yml,我在上面作为示例引用了它。

这个例子使用了我的php7.2镜像作为官方php7.2-fpm-alpine镜像的扩展,但是如果您不需要那么多额外的库,那么您可以构建官方镜像的扩展并使用它。 LAN的其余图像完全是原始的和正式的。

图像 -指示要下载的图像。 例如(rabbitmq:3.7.7-management-alpine)。

ports-指定容器将使用的端口(请参阅映像文档)。 默认情况下,示例nginx端口为80。 因此,如果要使用端口80,则必须在此处指定80:80,并且您的站点将在localhost上可用。 或者,您可以指定7777:80,然后您的站点将位于url localhost:7777。 这是必要的,以便可以在同一主机上部署多个项目。

-共享目录在此处指示。 例如,您的项目位于〜/ projects / my-sf4-app目录中,并且php容器配置为与/ app目录一起使用(与/ var / www / my-sf4-app中的目录相同)。 因此,对于容器而言,访问该项目将是方便的。 因此,在卷中,我们编写~/projects/my-sf4-app:/app (请参见上面的docker-compose.yml中的示例(我用相对方式../:/app表示)。

因此,该文件夹将为容器共享,并且将能够在其中执行各种操作,例如php bin/console doctrine:migrations:migrate 。 使用这些目录来保存应用程序数据也很方便。 例如,在postgresql中,您可以指定用于存储数据库数据的目录,然后在重新创建容器时,无需滚动转储或固定装置。

working_dir-指示容器的工作目录。 在这种情况下,请使用/ app(或类似于无业游民的/ var / www / my-sf4-app)。

环境 -容器的所有变量都在此处传递。 例如,对于rabbitmq,将发送用户名和密码,对于postgresql,将传递基本名称,用户名和密码。

container_name是一个可选字段,但是为了连接到容器,我更愿意指定。 如果未指定,将分配带有哈希的默认名称。

这些是必须指定的主要参数。 其余的可以是可选的,用于其他设置,或根据容器的文档。

现在,要开始所有这些操作,您需要在docker-compose文件所在的目录中运行docker-compose up -d

如何以及在哪里为局域网存储所有这些?


对于局域网,我使用项目根目录中的docker文件夹。


它包含数据文件夹,我在其中存储了所有信息postgresql和elasticsearch,因此,当您重新创建项目时,不必从头开始滚动固定装置。 还有一个Nginx爸爸,我在其中存储本地Nginx容器的配置。 我将docker-compose.yml中的这些文件夹与容器中的相应文件和文件夹同步。 我也认为编写用于与docker一起工作的bash脚本非常方便。 例如,start.sh脚本启动容器,然后安装作曲家,清理缓存并迁移。 对于项目同事来说,这也很方便,他们无需执行任何操作,只需运行脚本即可正常运行。

Start.sh脚本示例

 #!/usr/bin/env bash green=$(tput setf 2) toend=$(tput hpa $(tput cols))$(tput cub 6) echo -n '   ?: ' read name echo "  $name!       tutmesto.ru" echo -n "$name,      ? (y/n): " read use_dump echo '    !' docker-compose up -d || exit echo -en '\n' echo -n "  ! ${green}${toend}[OK]" echo -en '\n' echo '    .' ./composer-install.sh echo -en '\n' echo -n "   ${green}${toend}[OK]" echo -en '\n' echo '     40 ,    postgres-' sleep 5 echo '  35 ...' sleep 5 echo '  30 ...' sleep 5 echo '  25 ...' sleep 5 echo '  20 ...' sleep 5 echo '  15 ...' sleep 5 echo '  10 ...' sleep 5 echo '  5 ...' sleep 5 echo ' .   postgres-        !' case "$use_dump" in y|Y) ./dump.sh echo -en '\n' echo -n "  ! ${green}${toend}[OK]" echo -en '\n' ;; *) echo "$name, ,   ! =)" ;; esac echo '    !' ./migrations-migrate.sh echo -en '\n' echo -n "  ! ${green}${toend}[OK]" echo -en '\n' echo '  !' ./php-fpm-command.sh rm -rf var/cache/* ./php-fpm-command.sh chmod 777 var/ -R ./cache-clear.sh echo -en '\n' echo -n "  ! ${green}${toend}[OK]" echo -en '\n' echo '    !' ./env.sh echo -en '\n' echo -n "   ! ${green}${toend}[OK]" echo -en '\n' echo ", $name,    !    localhost:7777  !" echo -en '\n' echo "------------------------------------------------------------------------------" echo -en '\n' echo "    :" echo "./cache-clear.sh |  symfony 4" echo "./composer.sh [command(ex. install)] |  " echo "./composer-install.sh | composer install" echo "./connect-to-php-fpm.sh |   php" echo "./console.sh [command(ex. cache:clear)] |  php bin/console" echo "./destroy.sh |  .    ." echo "./dump.sh | ,     (dump.sql)" echo "./env.sh |   " echo "./migrations-migrate.sh | " echo "./php-fpm-command.sh [command(ex. php -m)] |   php-fpm " echo "./start.sh |  ( )" echo "./stop.sh |Gracefull shutdown " echo -en '\n' echo "        :" echo "client@c.cc | QWEasd123" echo "admin@a.aa | QWEasd123" echo "moderator@m.mm | QWEasd123" echo -en '\n' echo "------------------------------------------------------------------------------" echo -en '\n' echo -en '\n' echo 'OtezVikentiy brain corporation!' echo -en '\n' echo -en '\n' 

php-fpm-command.sh脚本示例

 #!/usr/bin/env bash cd "`dirname \"$0\"`" && \ docker-compose exec -T "php-fpm" sh -c "cd /app && $*" 

Connect-to-php-fpm.sh脚本示例

 #!/usr/bin/env bash docker exec -i -t --privileged php-fpm bash 

本地开发环境到此结束。 恭喜,您可以与同事分享最终结果! )

高产的


准备工作


假设您已经在LAN上编写了一些内容,并希望将其放在生产服务器或测试服务器上。 您可以在KVM虚拟化中托管服务器,或者在隔壁的房间中使用空调托管服务器。

要部署产品或Beta-服务器必须安装操作系统(最好是linux)和docker。 Docker的安装方式与LAN上的安装方式相同,没有区别。

Docker的生产力与LAN略有不同。 首先,您不能只接受并指定密码和其他信息以及docker-compose。 其次,您不能直接使用docker-compose。

Docker使用docker swarm和docker stack来提高生产力。 如果正确使用此系统,则该系统仅在其他命令上有所不同,并且docker swarm是群集的负载平衡器(再次有点抽象,但更易于理解)。

PS:我建议您练习在Vagrant上设置docker swarm(无论这听起来有多矛盾)。 培训的简单方法-使用与产品中相同的操作系统来挑选一个空的Vagrant,并将其配置为启动。

要配置docker swarm,您只需要运行一些命令:

 docker swarm init --advertise-addr 192.168.***.** (ip-  ) mkdir /app (          app) chown docker /app (     ) docker stack deploy -c docker-compose.yml my-first-sf4-docker-app 

现在,我们将更详细地考虑所有这些。

docker swarm init --advertise-addr-它直接启动docker swarm本身,并弄乱链接,以便您可以将其他服务器连接到该“群集”,以便它们在群集中工作。
mkdir / app && chown ..-您必须预先创建所有必要的目录,以使docker能够正常工作,以便在构建过程中不会抱怨缺少目录。
docker stack deploy -c docker-compose.yml my-first-sf4-docker-app-此命令将启动应用程序本身的汇编,这类似于docker-compose up -d,仅适用于docker swarm。

要开始任何程序集,您需要使用相同的docker-compose.yaml,但已针对生产性/ Beta版进行了少许修改。

 version: '3.1' services: php-fpm: image: otezvikentiy/php7.2-fpm:0.0.11 ports: - '9000:9000' networks: - my-test-network depends_on: - postgres - rabbitmq volumes: - /app:/app working_dir: /app deploy: replicas: 1 restart_policy: condition: on-failure placement: constraints: [node.role == manager] nginx: image: nginx:1.15.0 networks: - my-test-network working_dir: /app ports: - '80:80' depends_on: - php-fpm volumes: - /app:/app - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf deploy: replicas: 1 restart_policy: condition: on-failure placement: constraints: [node.role == manager] postgres: image: postgres:9.6 ports: - '5432:5432' working_dir: /app networks: - my-test-network secrets: - postgres_db - postgres_user - postgres_pass environment: POSTGRES_DB_FILE: /run/secrets/postgres_db POSTGRES_USER_FILE: /run/secrets/postgres_user POSTGRES_PASSWORD_FILE: /run/secrets/postgres_pass volumes: - ./data/dump:/app/dump - ./data/postgresql:/var/lib/postgresql/data deploy: replicas: 1 restart_policy: condition: on-failure placement: constraints: [node.role == manager] rabbitmq: image: rabbitmq:3.7.5-management networks: - my-test-network working_dir: /app hostname: my-test-sf4-app-rabbit-mq volumes: - /app:/app ports: - '5672:5672' - '15672:15672' secrets: - rabbitmq_default_user - rabbitmq_default_pass - rabbitmq_default_vhost environment: RABBITMQ_DEFAULT_USER_FILE: /run/secrets/rabbitmq_default_user RABBITMQ_DEFAULT_PASS_FILE: /run/secrets/rabbitmq_default_pass RABBITMQ_DEFAULT_VHOST_FILE: /run/secrets/rabbitmq_default_vhost deploy: replicas: 1 restart_policy: condition: on-failure placement: constraints: [node.role == manager] elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:6.3.0 networks: - my-test-network depends_on: - postgres environment: - discovery.type=single-node - discovery.zen.ping.unicast.hosts=elasticsearch - bootstrap.memory_lock=true - ES_JAVA_OPTS=-Xms512m -Xmx512m ports: - 9200:9200 - 9300:9300 working_dir: /app volumes: - /app:/app - ./data/elasticsearch:/usr/share/elasticsearch/data deploy: replicas: 1 restart_policy: condition: on-failure placement: constraints: [node.role == manager] jenkins: image: otezvikentiy/jenkins:0.0.2 networks: - my-test-network ports: - '8080:8080' - '50000:50000' volumes: - /app:/app - ./data/jenkins:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock - /usr/bin/docker:/usr/bin/docker deploy: replicas: 1 restart_policy: condition: on-failure placement: constraints: [node.role == manager] volumes: elasticsearch: postgresql: jenkins: networks: my-test-network: secrets: rabbitmq_default_user: file: ./secrets/rabbitmq_default_user rabbitmq_default_pass: file: ./secrets/rabbitmq_default_pass rabbitmq_default_vhost: file: ./secrets/rabbitmq_default_vhost postgres_db: file: ./secrets/postgres_db postgres_user: file: ./secrets/postgres_user postgres_pass: file: ./secrets/postgres_pass 

如您所见,产品的设置文件与LAN的文件略有不同。 它增加了机密,部署和网络。

机密 -用于存储密钥的文件。 密钥的创建非常简单。 您使用密钥名称创建文件-在其中写入值。 之后,在docker-compose.yml中,指定secrets部分并传输带有密钥的文件的整个列表。 更多细节
网络 -这会创建一个特定的内部网格,容器可以通过这些内部网格相互通信。 在局域网上-这是自动完成的,但在生产上-需要手动进行一些操作。 另外,您可以指定默认设置以外的其他设置。 更多细节
部署是局域网和产品/测试版之间的主要区别。

  deploy: replicas: 1 restart_policy: condition: on-failure placement: constraints: [node.role == manager] 

最小战斗机设置:

副本 -指示您需要运行的副本数量(实际上,如果您有集群并且使用了docker的负载均衡器,则使用此副本)。 例如,您有两个服务器,并通过docker swarm连接它们。 例如,在此处指定数字2,将在1台服务器上创建1个实例,在第二台服务器上创建第二个实例。 因此,服务器上的负载将被分成两半。
restart_policy-如果容器由于某种原因掉落,自动“重新升高”容器的策略。
放置 -容器实例的位置。 例如,有时您希望容器的所有实例仅在5台服务器中的1台上旋转,而不在它们之间分配。

我想阅读文档!

因此,我们在区分LAN的docker-compose.yaml和产品/测试版方面有一些改进。 现在,让我们尝试经营这项业务。

假设您正在Vagrant上进行培训,并且在服务器的根目录中已经具有docker-compose.yml产品的配置文件

 sudo apt-get update sudo apt-get -y upgrade sudo apt-get install -y language-pack-en-base export LC_ALL=en_US.UTF-8 export LANGUAGE=en_US.UTF-8 export LANG=en_US.UTF-8 curl -sSl https://get.docker.com/ | sh sudo usermod -aG docker ubuntu sudo apt-get install git sudo docker swarm init --advertise-addr 192.168.128.77 sudo mkdir /app sudo chmod 777 /app -R docker stack deploy -c /docker-compose.yml my-app git clone git@bitbucket.org:JohnDoe/my-app.git /app docker stack ps my-app docker stack ls docker stack services my-app 

PS:不要为sudo和777踢球,当然在生产力上不值得这样做。这只是为了学习速度。

因此,我们对与docker相关的行最感兴趣。
首先,我们初始化“ swarm”(docker swarm)。
然后,我们创建工作所需的目录。
在/ app目录中下载包含我们SF4代码的萝卜。
之后,有三个命令:ps,ls和services。

他们每个人都有自己的用处。我经常使用ps,因为它显示了容器的状态以及部分错误(如果有)。

假设容器已经上升,但是其中一些容器不断因错误而崩溃,并且在docker stack ps my-app中,您看到大量重启。要查看跌倒的原因,您需要运行docker container ps -a-,然后将出现一个不断掉落的容器。同一容器将有许多实例,例如my-app_php-fpm.1。*一些剧烈的哈希*。

因此,现在,知道了容器的名称,就可以执行docker日志my-app_php-fpm.1.。更正错误,然后重新启动所有内容。要使所有容器爆炸,可以执行以下操作:

 docker stack rm my-app 

之后,您将拥有一个干净的无群的容器。修复错误-并再次将docker stack deploy -c docker-compose.yml my-app修复。

Source: https://habr.com/ru/post/zh-CN420673/


All Articles