使用Ansible和Jenkins将Docker化的Symfony应用部署到生产中

127 阅读4分钟

在这个例子中,我们要用Ansible和Jenkins将一个Docker化的Symfony应用部署到生产服务器上。当涉及到生产环境时,总是很棘手,所以Docker相关的文件与开发环境以及其他构建文件相比,有点不同。你会看到下面的区别。

Jenkins服务器的必备条件

确保你涵盖了以下几点。

  • 完成了GitHub集成。

  • Ansible已经安装

  • Jenkins用户可以运行ansible 命令。

  • Ansible可以通过SSH进入生产服务器。

  • 在终端上登录Docker Hub,这样jenkins 用户就可以推送镜像。

我们有Jenkins和生产,详情如下。

  • Jenkins: 192.168.99.40/jenkins (IP/用户)。

  • 生产: 192.168.99.30/vagrant (IP/用户)。

我们也有一个MySQL服务器(而不是一个容器),详情如下。

DB_VERS=5.7.24

生产服务器的先决条件

准备SSH GitHub集成,并确保它可以运行docker 命令。

流程

Jenkins和GitHub的集成已经设置好了,所以当我把一个PR合并到master 分支时,GitHub会与Jenkins沟通,在那里运行部署管道。

**替代方案。**这个例子利用git将仓库克隆到生产服务器上,并构建docker镜像/容器。然而,如果你更喜欢把应用程序复制到容器图像中,并消除存储库克隆步骤,你可以阅读这篇文章。在这种情况下,你的Jenkinsfile将改变为构建和推送图像到Docker Hub。之后,一个阶段需要把相关的docker文件复制到生产服务器,然后运行docker pull/build/up命令。

  1. Jenkins检查到master 分支。

  2. Ansible SSH进入生产服务器并开始部署过程。

  3. 创建应用程序目录。

  4. 克隆应用程序仓库。

  5. 拷贝金库加密的.env 文件。

  6. 启动应用程序。

  7. 安装secure-delete 包。

  8. 安全地删除所有复制过来的东西。

MySQL服务器的先决条件

下面的步骤是一次性的,必须在一切之前完成。

允许外部连接

修改bind-address ,如下所示。

mysql-server$ cat /etc/mysql/my.cnf | grep bind-address

mysql-server$ mysql -u root -proot 登录到MySQL服务器,并做以下工作。

新的数据库和有权限的用户

创建一个名为sport 的新数据库。

mysql> CREATE DATABASE `sport` CHARACTER SET `UTF8mb4` COLLATE `utf8mb4_unicode_ci`;

创建一个新的用户inanzzz ,在sport 数据库上拥有所有权限。

mysql> USE mysql;

结构

我将列出dev 环境文件,只是为了让你与prod 环境文件进行比较,以便你知道它们有什么不同。主要的区别在于Dockerfile、Makefile和docker-compose.yml文件。

.

开发文件

docker/dev/docker-compose.yml

version: "3.4"

docker/dev/Makefile

PHP_SERVICE := football_php

docker/dev/nginx/app.conf

server {

docker/dev/nginx/nginx.conf

user nginx;

docker/dev/nginx/Dockerfile

FROM nginx:1.15.8-alpine

docker/dev/php/php.ini

[PHP]

docker/dev/php/www.conf

[global]

docker/dev/php/Dockerfile

FROM php:7.2.13-fpm-alpine3.8

程序文件

docker/prod/docker-compose.yml

如果我们不为每次部署的 "命名卷 "使用不同的名称,我们在应用程序代码中所做的修改将无法在生产中使用。因此,我用来自Jenkins的BUILD_ID 环境变量作为它的后缀。Ansible playbook将其传递给Makefile,然后Makefile将其传递给docker-compose.yml。另外,我把应用程序的源代码放在名为football_php 的 "php容器 "中,让nginx服务通过 "命名卷 "访问它。

重要提示: net.core.somaxconn 是可选的,但很重要。这是一个Linux内核参数,指定了TCP/IP套接字的最大积压量。默认值是128 。基于我们的修改,内核将允许一次最多有1000 个连接被 "积压"。这个值不能超过USHRT_MAX常数的最大限制,即65535 。在/usr/local/etc/php-fpm.d/www.conf 文件中的listen.backlog 选项也是同样的值。你可以在PHP-FPM容器中运行sysctl net.core.somaxconn 命令来验证它。简而言之,如果你期望有太多的并发请求,那么最好保持高值。我用$ ab -n 10000 -c 1000 http://0.0.0.0:80/ 命令总共发送了10000:1000 (total:concurrent)个请求,全部通过。如果我把这个值降低到500 ,那么大约有300个请求失败。另一个例子,Redis需要很多连接,所以它也需要一个高数值。

version: "3.4"

docker/prod/Makefile

build:

docker/prod/nginx/app.conf

不要像我在生产中那样忽略了SSL证书和端口443!

server {

docker/prod/nginx/nginx.conf

user nginx;

docker/prod/nginx/Dockerfile

FROM nginx:1.15.8-alpine

docker/prod/php/php.ini

[PHP]

docker/prod/php/www.conf

[global]

docker/prod/php/Dockerfile

FROM php:7.2.13-fpm-alpine3.8

CICD文件

cicd/merge/master/Jenkinsfile

pipeline {

cicd/provision/prod/hosts.yml

如果你愿意,你可以用Ansible vault来加密。

all:

cicd/provision/prod/site.yml

如果你愿意,你可以在这里准备MySQL服务器,也可以使用一些环境变量。

---

其他文件

.dockerignore

cicd/

.env

APP_ENV=dev

config/packages/doctrine.yaml

doctrine:

DefaultContainer.php

class EnvironmentController

Ansible Vault 加密

生产环境的.env 文件包含秘密/重要的值,所以我们需要在Jenkins服务器上创建它,并使用ansible-vault 命令对其进行加密。当部署开始时,它将在幕后被解密(在stage('Deploy') )并传输到生产服务器。这些值将被设置为php容器中的环境变量,我们的Symfony应用程序将使用它们而不是在.env 文件中设置的相同变量。这就是它的工作原理,环境变量有优先权!

jenkins@jenkins-server:$ mkdir -p ~/app-secrets/sport

实际内容如下所示。

APP_ENV=prod

测试生产管道

假设我们已经合并了一个PR信息master 分支。

Running on Jenkins in /var/lib/jenkins/workspace/sport-merge-master

检查生产服务器

正如你所看到的,我们在开始时创建的/home/vagrant/sport ,在部署过程结束时已被删除。

vagrant@production:~$ ls -l
vagrant@production:~$ docker images
vagrant@production:~$ docker ps -a
vagrant@production:~$ docker volume ls
vagrant@production:~$ docker inspect football_source_code_17
vagrant@production:~$ sudo ls -l /var/lib/docker/volumes/football_source_code_17/_data

检查应用程序

vagrant@production:~$ curl -i http://0.0.0.0:80 # or http://0.0.0.0 | http://localhost