Today I will show you how to build an HTTP service in C++ using modern tools. The point of this article is to show how easy (or hard) it is to setup an environment for cross-platform C++ development, and package the binary in one of the modern distribution formats, container.
First, a disclaimer:
I am not a professional C++ developers, and any opinions shared in this tutorial should not be treated as absolute truth!
That said, since I am not profession C++ developer, this tutorial is suitable for "everyone-and-their-mother". The shell commands shown should work in most recent versions of Linux and OSX. Windows users can find themselves making some extra research, but overall the tools used are cross-platform.
Requirements
First things first, install the following tools:
- CMake - system designed to build cross-platform software.
- Conan - C/C++ package manager.
- Make - tool controlling the generation of executables.
- Docker - set of tools for OS-level virtualization.
What are we building?
The HTTP service and is mostly based on this particular example from Boost Beast library. I modified it, replacing plaintext responses with json, and adding Boost Log library.
Setting up development tools
Start by creating a src
directory in the root of your project, and putting this C++ file inside:
mkdir src
curl -o src/main.cpp https://gist.githubusercontent.com/hi-artem/07e896d5d7b43d1cf63cf58e8665524b/raw/73d3148603c16e0505af52071b7658e6eda967b4/main.cpp
Although Boost is one of the well supported libraries, I always struggle manually installing it on my development machine. This is where Conan comes into play. For people familiar with Node, think of Conan as NPM or Yarn for C/C++.
Create conanfile.txt
with the following content:
[requires]
boost/1.74.0
[generators]
cmake
Next, configure CMake to let it know about location of the C++ source file, and enable Conan integration. It can be done by creating CMakeLists.txt
looking like so:
cmake_minimum_required(VERSION 3.10)
project(http-service VERSION 0.0.1)
set(CMAKE_CXX_STANDARD 14)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(http-service src/main.cpp)
target_link_libraries(http-service ${CONAN_LIBS})
Building executable
This is all you need to build and run the project locally! The following shell script contains all the required build commands:
#!/bin/bash
# remove all build folder if exists
rm -rf build
# create new build folder
mkdir build && cd build
# install dependencies
conan install ..
# generate build files
cmake ..
# generate executable
make
Once build succeded the service can be started by running:
./build/bin/http-service 0.0.0.0 8080
You should see something like:
[2020-12-05 13:17:27.928789] [0x000000011670fdc0] [info] Service is listening on 0.0.0.0:8080
Use curl
or web browser, to test that service is running. For example:
curl -i 0.0.0.0:8000/api/status
## Should return something similar to:
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 60
{"status":"OK","request_count":"1","timestamp":"1607196274"}
Containerizing service
The previous steps configured development environment, however modern stacks require application to be packaged as container so it can be run by one of the orchestrators, like Kubernetes or Nomad.
Since we are using Docker to containerize the service, let's start by creating Dockerfile
:
# Create image with all required build tools
FROM ubuntu:18.04 as build-tools
RUN apt-get update && apt-get install -y \
build-essential \
cmake \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install conan
# Build binary
FROM build-tools as builder
WORKDIR /usr/src/service
COPY . .
WORKDIR /usr/src/service/build
RUN conan install ..
RUN cmake ..
RUN make
# Copy binary to fresh ubuntu image
FROM ubuntu:18.04 as runner
RUN groupadd -r ubuntu && useradd --no-log-init -r -g ubuntu ubuntu
USER ubuntu:ubuntu
COPY --from=builder /usr/src/service/build/bin/http-service /usr/bin/http-service
EXPOSE 8080
CMD [ "http-service", "0.0.0.0", "8080" ]
The image is created using multi-stage build process: first, creating the image with all required tools to build the service; second, build the service; finally, copy the executable in fresh ubuntu image. The process is run as non-root user to avoid privileges escalation.
The last thing before the image build, create .dockerignore
file, which serves similar purpose as .gitignore
:
build
.vscode
Finally, let's build the image. For folks no familiar with Docker, this commands will build and run the HTTP service:
docker build -t http-service .
docker run --rm --init -p 8080:8080 http-service
As previously, use curl
or web browser to test that service is running:
curl -i localhost:8000/api/status
## Should return something similar to:
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 60
{"status":"OK","request_count":"1","timestamp":"1607196274"}
At this point, the image can be tagged and pushed in a Docker registry, from where it can be pulled by a container orchestrator.
The purposes of this article was to reduce frustration associated with setting environment for C++ development. I hope you were able to follow. If not, the source code is available at github.com/hi-artem/http-service
. Let me know in comments if you have any questions.
Top comments (0)