Search code examples
phpamazon-web-servicesdockeramazon-linuxbref

Install 'ffi' PHP extension in Bref Laravel app running AWS Lambda with SAM template


Prerequisites

I'm using Bref to run my PHP Laravel app on AWS Lambda. I've had a requirement to use this tech stack and to set it up with SAM template (so I can't use serverless). Lambda is being build as a docker image, fetches from ECR and running as an app. It's the only way to make my script work with all Laravel dependencies. That actually worked well until the moment I've got a requirement to send Kafka messages in my Lambda Laravel app, which is didn't work without ffi extension installed (just enabling it in php.ini didn't work). I'm intended to use rd-kafka lib

Bref and SAM template setup

Initially I was thinking to add a Lambda layer for Kafka dependencies and ffi extension setup in particular. But the approach of setting up the SAM template with lambda function as an image restricts me from using Lambda layers, including Bref layers. Once the property PackageType: Image is set, then I can't use Layers or Handler properties as it returns an error during sam deploy

Installing ffi extension on Amazon Linux OS

This made me think that I should try to fit everything just inside the same image. I tried to install ffi extension via install-php-extensions. It wouldn't be a problem if I used default docker php-fpm image. But Bref makes me use specific base image bref/php-83, which is running on Amazon Linux OS (which is similar to Centos, it has yum package manager). This OS is not Ubuntu/Debian based, so I can't run that install-php-extensions script to install extensions as it returns an error that it's not compatible with this OS

So I just downloaded an appropriate version of php-src, installed all appropriate dependencies using yum (libxml2-devel, sqlite-devel, libffi-devel, php-devel, re2c, bison, autoconf, make, libtool, librdkafka). Unfortunately I was getting an error Unable to load dynamic library 'ffi' (tried: /opt/bref/extensions/ffi (/opt/bref/extensions/extensions/ffi: cannot open shared object file: No such file or directory), /opt/bref/extensions/ffi.so (libffi.so.7: cannot open shared object file: No such file or directory)) in Unknown on line 0. Even though I copied ffi.so file to /opt/bref/extensions/extensions. It exists and has appropriate permissions. I tried to run yum groupinstall "Development Tools" but it didn't help as well

Then I went to /usr/src/php-src/ext/ffi and ran /usr/bin/phpize to prepare this extension. Then I went pack to the php-src root, ran ./buildconf to generate configure file. Then I ran ./configure --with-ffi FFI_LIBS=/ext/ffi --with-config-file-scan-dir=/opt/bref/etc/php/conf.d --with-libdir=lib64 FFI_LIBS=/usr/lib64/ so I enabled ffi and included path to libffi.so. Then I ran make and it almost finished setting up, but in the very end I've got an error

ext/ffi/ffi.o: In function `zend_ffi_face_struct_add_fields':
/usr/src/php-src/ext/ffi/ffi.c:392: undefined reference to `ffi_type_pointer'
/usr/src/php-src/ext/ffi/ffi.c:389: undefined reference to `ffi_type_uint64'
/usr/src/php-src/ext/ffi/ffi.c:385: undefined reference to `ffi_type_uint32'
/usr/src/php-src/ext/ffi/ffi.c:381: undefined reference to `ffi_type_uint16'
/usr/src/php-src/ext/ffi/ffi.c:377: undefined reference to `ffi_type_uint8'
/usr/src/php-src/ext/ffi/ffi.c:370: undefined reference to `ffi_type_longdouble'
/usr/src/php-src/ext/ffi/ffi.c:366: undefined reference to `ffi_type_double'
/usr/src/php-src/ext/ffi/ffi.c:363: undefined reference to `ffi_type_float'
/usr/src/php-src/ext/ffi/ffi.c:409: undefined reference to `ffi_type_void'
ext/ffi/ffi.o: In function `zend_ffi_get_type':
/usr/src/php-src/ext/ffi/ffi.c:457: undefined reference to `ffi_type_sint8'
/usr/src/php-src/ext/ffi/ffi.c:455: undefined reference to `ffi_type_uint8'
/usr/src/php-src/ext/ffi/ffi.c:447: undefined reference to `ffi_type_float'
/usr/src/php-src/ext/ffi/ffi.c:463: undefined reference to `ffi_type_uint32'
/usr/src/php-src/ext/ffi/ffi.c:465: undefined reference to `ffi_type_sint32'
/usr/src/php-src/ext/ffi/ffi.c:467: undefined reference to `ffi_type_uint64'
/usr/src/php-src/ext/ffi/ffi.c:469: undefined reference to `ffi_type_sint64'
/usr/src/php-src/ext/ffi/ffi.c:459: undefined reference to `ffi_type_uint16'
/usr/src/php-src/ext/ffi/ffi.c:461: undefined reference to `ffi_type_sint16'
/usr/src/php-src/ext/ffi/ffi.c:452: undefined reference to `ffi_type_longdouble'
/usr/src/php-src/ext/ffi/ffi.c:473: undefined reference to `ffi_type_void'
/usr/src/php-src/ext/ffi/ffi.c:471: undefined reference to `ffi_type_pointer'
/usr/src/php-src/ext/ffi/ffi.c:449: undefined reference to `ffi_type_double'
ext/ffi/ffi.o: In function `zend_ffi_callback_hash_dtor':
/usr/src/php-src/ext/ffi/ffi.c:924: undefined reference to `ffi_closure_free'
ext/ffi/ffi.o: In function `zend_ffi_create_callback':
/usr/src/php-src/ext/ffi/ffi.c:1040: undefined reference to `ffi_closure_alloc'
/usr/src/php-src/ext/ffi/ffi.c:1087: undefined reference to `ffi_prep_cif'
/usr/src/php-src/ext/ffi/ffi.c:1092: undefined reference to `ffi_prep_closure_loc'
/usr/src/php-src/ext/ffi/ffi.c:1104: undefined reference to `ffi_closure_free'
ext/ffi/ffi.o: In function `zend_ffi_pass_arg':
/usr/src/php-src/ext/ffi/ffi.c:2581: undefined reference to `ffi_type_float'
/usr/src/php-src/ext/ffi/ffi.c:2586: undefined reference to `ffi_type_double'
/usr/src/php-src/ext/ffi/ffi.c:2592: undefined reference to `ffi_type_double'
/usr/src/php-src/ext/ffi/ffi.c:2598: undefined reference to `ffi_type_uint8'
/usr/src/php-src/ext/ffi/ffi.c:2603: undefined reference to `ffi_type_sint8'
/usr/src/php-src/ext/ffi/ffi.c:2608: undefined reference to `ffi_type_uint16'
/usr/src/php-src/ext/ffi/ffi.c:2613: undefined reference to `ffi_type_sint16'
/usr/src/php-src/ext/ffi/ffi.c:2618: undefined reference to `ffi_type_uint32'
/usr/src/php-src/ext/ffi/ffi.c:2623: undefined reference to `ffi_type_sint32'
/usr/src/php-src/ext/ffi/ffi.c:2628: undefined reference to `ffi_type_uint64'
/usr/src/php-src/ext/ffi/ffi.c:2633: undefined reference to `ffi_type_sint64'
/usr/src/php-src/ext/ffi/ffi.c:2676: undefined reference to `ffi_type_uint8'
/usr/src/php-src/ext/ffi/ffi.c:2681: undefined reference to `ffi_type_sint8'
/usr/src/php-src/ext/ffi/ffi.c:2637: undefined reference to `ffi_type_pointer'
/usr/src/php-src/ext/ffi/ffi.c:2681: undefined reference to `ffi_type_sint8'
ext/ffi/ffi.o: In function `zif_ffi_trampoline':
/usr/src/php-src/ext/ffi/ffi.c:2853: undefined reference to `ffi_prep_cif'
/usr/src/php-src/ext/ffi/ffi.c:2853: undefined reference to `ffi_prep_cif'
/usr/src/php-src/ext/ffi/ffi.c:2862: undefined reference to `ffi_call'
/usr/src/php-src/ext/ffi/ffi.c:2816: undefined reference to `ffi_prep_cif_var'
ext/ffi/ffi.o: In function `zend_ffi_pass_var_arg':
/usr/src/php-src/ext/ffi/ffi.c:2741: undefined reference to `ffi_type_pointer'
ext/ffi/ffi.o: In function `zif_ffi_trampoline':
/usr/src/php-src/ext/ffi/ffi.c:2816: undefined reference to `ffi_prep_cif_var'
ext/ffi/ffi.o: In function `zend_ffi_pass_var_arg':
/usr/src/php-src/ext/ffi/ffi.c:2737: undefined reference to `ffi_type_double'
/usr/src/php-src/ext/ffi/ffi.c:2724: undefined reference to `ffi_type_uint8'
/usr/src/php-src/ext/ffi/ffi.c:2732: undefined reference to `ffi_type_sint64'
/usr/src/php-src/ext/ffi/ffi.c:2720: undefined reference to `ffi_type_uint8'
/usr/src/php-src/ext/ffi/ffi.c:2716: undefined reference to `ffi_type_pointer'
collect2: error: ld returned 1 exit status
make: *** [sapi/cli/php] Error 1

My very last try was to download libffi and use it's path to include these variables which are missing due to the error above. So this time I ran ./configure --with-ffi --with-config-file-scan-dir=/opt/bref/etc/php/conf.d --with-libdir=lib64 FFI_LIBS=/usr/src/php-src/ect/ffi FFI_CFLAGS=/usr/src/libffi/include with updated options, using the path to my libffi include folder, which contains the scripts with appropriate variables. But it didn't work though, I'm still getting the same error on running make. The reason why I decided to download libffi repo separately, it's because it seems like once yum installs libffi, it only saves libffi.so in /usr/lib64 folder

Afterword

For the moment I have no more ideas, but to continue playing around with that FFI_LIBS and FFI_CFLAGS envs to make it work the proper way. If you have any ideas how to help me resolve these dependencies issue or how to make my Laravel Bref app work, please share your ideas with me. My end goal is running Laravel app with Bref on AWS Lambda with possibility of sending Kafka messages (so ffi extension should be enabled). For the moment my Bref app is working, but Kafka doesn't work. My Lambda is being triggered by SQS (but I'm not sure if it's important in current context)

UPDATE

Ok, now I've dealt with the error above by running this one ./configure --with-ffi --with-config-file-scan-dir=/opt/bref/etc/php/conf.d PKG_CONFIG_PATH=/usr/src. I kept my libffi in /usr/src so this one helped. But even though I managed to run make and make install, it's still complaining about the ffi.so absence, this one

Unable to load dynamic library 'ffi' (tried: /opt/bref/extensions/ffi (/opt/bref/extensions/ffi: cannot open shared object file: No such file or directory), /opt/bref/extensions/ffi.so (/opt/bref/extensions/ffi.so: cannot open shared object file: No such file or directory)) in Unknown on line 0

This makes me think that now I have a few PHP versions simulstaliously and I gotta switch them to make it work. But I'm still up to any proposals though!

PHP version 8.3.16

OS version: Amazon Linux 2


Solution

  • I've dealt with it with installing ffi extension. That's how my final Dockerfile looks like. I'm using build-php-83 bref image to build and install PHP extensions and then I copy generated .so files and libs to my lambda bref image

    ARG PHP_VERSION="83"
    ARG BREF_VERSION="2"
    FROM bref/build-php-$PHP_VERSION:$BREF_VERSION AS ext
    
    ENV LD_LIBRARY_PATH=/usr/lib:/usr/lib64:$LD_LIBRARY_PATH
    
    RUN yum install -y libffi-devel
    
    WORKDIR ${PHP_BUILD_DIR}/ext/ffi
    RUN echo $(pwd) && phpize && \
        ./configure \
        --with-ffi;
    
    RUN make -j $(nproc) install
    
    RUN cp `php-config --extension-dir`/ffi.so /tmp/ffi.so
    RUN echo 'extension=ffi.so' > /tmp/ext.ini
    
    RUN php /bref/lib-copy/copy-dependencies.php /tmp/ffi.so /tmp/extension-libs
    
    FROM bref/php-$PHP_VERSION:$BREF_VERSION as lambda-stage
    ADD . $LAMBDA_TASK_ROOT
    #
    COPY --from=ext /tmp/ffi.so /opt/bref/extensions/ffi.so
    COPY --from=ext /tmp/ext.ini /opt/bref/etc/php/conf.d/ext-ffi.ini
    COPY --from=ext /tmp/extension-libs /opt/lib
    
    COPY .env.staging .env
    
    RUN cat php/conf.d/php.ini >> /opt/bref/etc/php/conf.d/bref.ini
    
    CMD [ "app/Handlers/LambdaHandler.php" ]