Device or resource busy in alpine container, but not on ubuntu

  alpine, boost, boost-beast, c++, docker

As the title describes, I have a working executable in an Ubuntu container that I use to build my app, but once I copy it in an Alpine container, I get Device or resource busy with the same executable and I’m a bit confused on what is going on.

Here’s my dockerfile:

ARG ALPINE_VERSION=3.12
ARG UBUNTU_VERSION=20.04
FROM ubuntu:${UBUNTU_VERSION} as builder

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get -y install 
    curl 
    g++-10 
    gcc-10 
    ninja-build 
    unzip 
    zip 
    make 
    perl 
    pkg-config 
    git 
    gdb

RUN mkdir /opt/vcpkg 
    && curl -L -s "https://github.com/microsoft/vcpkg/tarball/8ce7b41302728ff6fc8bd377f572c4cbe8c64c1d" | tar --strip-components=1 -xz -C /opt/vcpkg 
    && /opt/vcpkg/bootstrap-vcpkg.sh

RUN mkdir /opt/cmake && 
    curl -s "https://cmake.org/files/v3.18/cmake-3.18.0-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /opt/cmake

RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10 && 
    update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10

ENV PATH="/opt/cmake/bin:/opt/vcpkg:${PATH}"

RUN mkdir /build2
COPY src2 /source/src2
WORKDIR /build2
RUN cmake /source/src2 
    -DCMAKE_BUILD_TYPE=Release 
    -DCMAKE_GENERATOR=Ninja 
    -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja 
    -DCMAKE_INSTALL_PREFIX=/install 
    -DCMAKE_TOOLCHAIN_FILE=/opt/vcpkg/scripts/buildsystems/vcpkg.cmake 
RUN cmake --build . --target all

FROM alpine:${ALPINE_VERSION}

COPY --from=builder /build2/http-beast-ssl /http-beast-ssl
COPY --from=builder /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2
COPY --from=builder /usr/lib/x86_64-linux-gnu/libdl.so.2 /usr/lib/x86_64-linux-gnu/libdl.so.2
COPY --from=builder /usr/lib/x86_64-linux-gnu/libpthread.so.0 /usr/lib/x86_64-linux-gnu/libpthread.so.0
COPY --from=builder /usr/lib/x86_64-linux-gnu/libstdc++.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6
COPY --from=builder /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28
COPY --from=builder /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
COPY --from=builder /usr/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc.so.6
COPY --from=builder /usr/lib/x86_64-linux-gnu/libm.so.6 /usr/lib/x86_64-linux-gnu/libm.so.6
COPY --from=builder /usr/share/ca-certificates /usr/share/ca-certificates
COPY --from=builder /etc/ssl/ /etc/ssl/

ENTRYPOINT /http-beast-ssl

When I run http-beast-ssl example.com 443 / in the ubuntu container, everything work fine. I get the HTML back in the console as expected.

When I do the same thing inside the Alpine container, then I get the error.

What is going on there and how can I fix it? Is there something needed to make networking work as expected in my container?

Here’s the C++ source code as reference:

#include <boost/asio/ssl.hpp>
#include <boost/lexical_cast.hpp>
#include <openssl/cryptoerr.h>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/beast/version.hpp>
#include <openssl/opensslv.h>
#include <cstdlib>
#include <iostream>
#include <string>

namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http;   // from <boost/beast/http.hpp>
namespace net = boost::asio;    // from <boost/asio.hpp>
namespace ssl = net::ssl;       // from <boost/asio/ssl.hpp>
using tcp = net::ip::tcp;       // from <boost/asio/ip/tcp.hpp>

// Performs an HTTP GET and prints the response
int main(int argc, char** argv)
{
    std::cout << OPENSSL_VERSION_TEXT << std::endl;
    try
    {
        // Check command line arguments.
        if(argc != 4 && argc != 5)
        {
            std::cerr <<
                "Usage: http-client-sync-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]n" <<
                "Example:n" <<
                "    http-client-sync-ssl www.example.com 443 /n" <<
                "    http-client-sync-ssl www.example.com 443 / 1.0n";
            return EXIT_FAILURE;
        }
        auto const host = argv[1];
        auto const port = argv[2];
        auto const target = argv[3];
        int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;

        // The io_context is required for all I/O
        net::io_context ioc;

        // The SSL context is required, and holds certificates
        ssl::context ctx(ssl::context::tlsv12_client);

        // This holds the root certificate used for verification
        ctx.set_default_verify_paths();

        // Verify the remote server's certificate
        ctx.set_verify_mode(ssl::verify_peer);

            // These objects perform our I/O
        tcp::resolver resolver(ioc);
        beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);

        // Set SNI Hostname (many hosts need this to handshake successfully)
        if(! SSL_set_tlsext_host_name(stream.native_handle(), host))
        {
            beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
            throw beast::system_error{ec};
        }

        // Look up the domain name
        auto const results = resolver.resolve(host, port);

        // Make the connection on the IP address we get from a lookup
        beast::get_lowest_layer(stream).connect(results);

        // Perform the SSL handshake
        stream.handshake(ssl::stream_base::client);

        // Set up an HTTP GET request message
        http::request<http::string_body> req{http::verb::get, target, version};
        req.set(http::field::host, host);
        req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);

        // Send the HTTP request to the remote host
        http::write(stream, req);

        // This buffer is used for reading and must be persisted
        beast::flat_buffer buffer;

        // Declare a container to hold the response
        http::response<http::dynamic_body> res;

        // Receive the HTTP response
        http::read(stream, buffer, res);

        // Write the message to standard out
        std::cout << res << std::endl;

        // Gracefully close the stream
        beast::error_code ec;
        stream.shutdown(ec);
        if(ec == net::error::eof)
        {
            // Rationale:
            // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
            ec = {};
        }
        if(ec)
            throw beast::system_error{ec};

        // If we get here then the connection is closed gracefully
    }
    catch(boost::system::system_error const& e)
    {
        auto const& error = e.code();
        std::string err = error.message();
        if (error.category() == boost::asio::error::get_ssl_category()) {
            err = std::string(" (")
                +boost::lexical_cast<std::string>(ERR_GET_LIB(error.value()))+","
                +boost::lexical_cast<std::string>(ERR_GET_FUNC(error.value()))+","
                +boost::lexical_cast<std::string>(ERR_GET_REASON(error.value()))+") ";
    
            //ERR_PACK /* crypto/err/err.h */
            char buf[128];
            ::ERR_error_string_n(error.value(), buf, sizeof(buf));
            err += buf;
            std::cerr << err << std::endl;
        } else {
            std::cerr << err << std::endl;
        }
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
    
}

And here’s my vcpkg.json and CMakeLists.txt files:

{
    "name": "mypackage",
    "version-string": "0.1.0-dev",
    "dependencies": [
        "boost-asio",
        "boost-beast",
        "openssl"
    ]
}
cmake_minimum_required(VERSION 3.18)
project(beast-test)

set(CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY ON)
set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF)

find_package(Boost 1.74 REQUIRED COMPONENTS system)
find_package(Threads REQUIRED)
find_package(OpenSSL REQUIRED)

add_executable(http-beast-ssl main2.cpp)
target_link_libraries(http-beast-ssl PRIVATE Threads::Threads Boost::system OpenSSL::SSL OpenSSL::Crypto)

Source: Windows Questions C++

LEAVE A COMMENT