The main drawback of building microservices inside an image is that all of the sources and build artifacts will be available for anyone who has access to a Docker image. If you want to remove sources and other building artifacts, you can use one of two approaches.
- The first approach is to build all sources using a Docker image with the Rust compiler, providing access to sources through a linked virtual volume. In Docker, you can map any local folder to a volume inside a container using the -v argument of the docker run command. The disadvantage of this approach is that Docker uses another ID inside the container that you have in your local session. It can create files you can't delete without changing the user ID. Also, this approach is harder to maintain. But it's useful if you need the result of compilation only. If you plan to run a microservice inside a container, it's better to build everything inside an image.
- The second approach involves building everything with Docker, but using a building cache to get the compilation result and putting it into a newly created container. Let's explore the Dockerfile that implements this approach:
FROM rust:nightly as builder
RUN USER=root cargo new --bin dbsync-worker
WORKDIR /dbsync-worker
COPY ./Cargo.toml ./Cargo.toml
RUN cargo build
RUN rm src/*.rs
COPY ./src ./src
COPY ./migrations ./migrations
COPY ./diesel.toml ./diesel.toml
RUN rm ./target/debug/deps/dbsync_worker*
RUN cargo build
FROM buildpack-deps:stretch
COPY --from=builder /dbsync-worker/target/debug/dbsync-worker /app/
ENV RUST_LOG=debug
EXPOSE 8000
ENTRYPOINT ["/app/dbsync-worker"]
We used the Dockerfile of the dbsync microservice and the first part of the file was the same as the original with one small improvement—we set that name as an image we built in the first line:
FROM rust:nightly as builder
Now we can use the cached data of the image using the builder name.
After this section, we start a new empty image from the buildpack-deps image that was originally used to build the preceding rust:nightly image. We copy a binary executable file from the builder image using the COPY command with the --from parameter where we set the name of the image:
COPY --from=builder /dbsync-worker/target/debug/dbsync-worker /app/
This command copies the binary to the /app folder inside the image and we can use it as the entry point of the container:
ENTRYPOINT ["/app/dbsync-worker"]
We also set the RUST_LOG environment variable and expose the port. Build this image by passing the name of this Dockerfile with the -f argument of the Docker build command and you will get an image with a single binary of the microservice inside. In other words, this approach allows us to build a microservice and reuse the compiled binary for a new image. You now know enough to pack your microservices to an image and now we can explore Docker Compose's ability to start a set of microservices and connect all launched containers to each other.