From 6a678944a00dfba15ce2373493508d1924a2cf24 Mon Sep 17 00:00:00 2001 From: Roberto Alsina Date: Sun, 30 Jun 2024 10:18:51 -0300 Subject: [PATCH] Cleanup image building code, refactored, fewer exposed ports, templated files in runtime --- Dockerfile | 4 +- Makefile | 2 +- .../crystal/{Dockerfile => Dockerfile.j2} | 6 +- shard.lock | 8 +- shard.yml | 2 + src/faaso.cr | 30 +------- src/funko.cr | 74 ++++++++++++++----- 7 files changed, 70 insertions(+), 56 deletions(-) rename runtimes/crystal/{Dockerfile => Dockerfile.j2} (68%) diff --git a/Dockerfile b/Dockerfile index a9bad16..2638cab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine as build -RUN apk add crystal shards yaml-dev openssl-dev zlib-dev +RUN apk add crystal shards yaml-dev openssl-dev zlib-dev libxml2-dev RUN addgroup -S app && adduser app -S -G app WORKDIR /home/app COPY shard.yml ./ @@ -10,7 +10,7 @@ RUN shards build -d --error-trace RUN strip bin/faaso-daemon FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine as ship -RUN apk add tinyproxy multirun openssl zlib yaml pcre2 gc libevent libgcc +RUN apk add tinyproxy multirun openssl zlib yaml pcre2 gc libevent libgcc libxml2 # Unprivileged user RUN addgroup -S app && adduser app -S -G app diff --git a/Makefile b/Makefile index f1d6cac..fbcae11 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ build: shard.yml $(wildcard src/**/*cr) proxy-image: build docker build . -t faaso-proxy --no-cache start-proxy: - docker run --network=faaso-net -v /var/run/docker.sock:/var/run/docker.sock -p 8888:8888 -p 3000:3000 faaso-proxy + docker run --network=faaso-net -v /var/run/docker.sock:/var/run/docker.sock -p 8888:8888 faaso-proxy .PHONY: build proxy-image start-proxy diff --git a/runtimes/crystal/Dockerfile b/runtimes/crystal/Dockerfile.j2 similarity index 68% rename from runtimes/crystal/Dockerfile rename to runtimes/crystal/Dockerfile.j2 index e7f8fa1..f6ef42a 100644 --- a/runtimes/crystal/Dockerfile +++ b/runtimes/crystal/Dockerfile.j2 @@ -1,6 +1,6 @@ FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine as build -RUN apk update && apk upgrade && apk add crystal shards openssl-dev zlib-dev && apk cache clean +RUN apk update && apk upgrade && apk add crystal shards openssl-dev zlib-dev {{ ship_packages | join(" ") }} {{ devel_packages | join(" ") }} && apk cache clean WORKDIR /home/app @@ -10,7 +10,7 @@ RUN shards build --release RUN strip bin/* FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine as ship -RUN apk update && apk upgrade && apk add openssl pcre2 libgcc gc libevent curl && apk cache clean +RUN apk update && apk upgrade && apk add openssl pcre2 libgcc gc libevent curl {{ ship_packages | join " " }} && apk cache clean RUN addgroup -S app && adduser app -S -G app WORKDIR /home/app @@ -19,4 +19,4 @@ USER app COPY --from=build /home/app/bin/function . CMD ["./function"] -HEALTHCHECK CMD curl --fail http://localhost:3000/ping || exit 1 \ No newline at end of file +HEALTHCHECK {{ healthcheck_options }} CMD {{ healthcheck_command }} \ No newline at end of file diff --git a/shard.lock b/shard.lock index 6b250f5..450a741 100644 --- a/shard.lock +++ b/shard.lock @@ -8,6 +8,10 @@ shards: git: https://github.com/mrrooijen/commander.git version: 0.4.0 + crinja: + git: https://github.com/straight-shoota/crinja.git + version: 0.8.1 + crystar: git: https://github.com/naqvis/crystar.git version: 0.4.0 @@ -24,10 +28,6 @@ shards: git: https://github.com/kemalcr/kemal.git version: 1.5.0 - kiwi: - git: https://github.com/ralsina/kiwi.git - version: 0.1.0+git.commit.65059b87771238593a28b2b8b9fdf77d8e2b9e89 - radix: git: https://github.com/luislavena/radix.git version: 0.4.1 diff --git a/shard.yml b/shard.yml index 89d240e..23204d2 100644 --- a/shard.yml +++ b/shard.yml @@ -23,3 +23,5 @@ dependencies: github: mrrooijen/commander kemal: github: kemalcr/kemal + crinja: + github: straight-shoota/crinja \ No newline at end of file diff --git a/src/faaso.cr b/src/faaso.cr index fcf886a..7c82f19 100644 --- a/src/faaso.cr +++ b/src/faaso.cr @@ -2,7 +2,6 @@ require "./funko.cr" require "commander" require "docr" require "docr/utils.cr" -require "file_utils" require "uuid" # Functions as a Service, Ops! @@ -36,38 +35,13 @@ module Faaso def run funkos = Funko.from_paths(@arguments) funkos.each do |funko| - # FIXME: refactor lots of this into the Funko class # Create temporary build location tmp_dir = Path.new("tmp", UUID.random.to_s) Dir.mkdir_p(tmp_dir) unless File.exists? tmp_dir - - # Copy runtime if requested - if !funko.runtime.nil? - runtime_dir = Path.new("runtimes", funko.runtime.to_s) - if !File.exists? runtime_dir - puts "Error: runtime #{funko.runtime} not found" - next - end - Dir.glob("#{runtime_dir}/*").each { |src| - FileUtils.cp_r(src, tmp_dir) - } - end - - # Copy funko - if funko.path.empty? - puts "Internal error: empty funko path for #{funko.name}" - next - end - Dir.glob("#{funko.path}/*").each { |src| - FileUtils.cp_r(src, tmp_dir) - } + funko.prepare_build tmp_dir puts "Building function... #{funko.name} in #{tmp_dir}" - - docker_api = Docr::API.new(Docr::Client.new) - docker_api.images.build( - context: tmp_dir.to_s, - tags: ["#{funko.name}:latest"]) { } + funko.build tmp_dir end end end diff --git a/src/funko.cr b/src/funko.cr index 7ca781a..6eb169f 100644 --- a/src/funko.cr +++ b/src/funko.cr @@ -1,3 +1,5 @@ +require "crinja" +require "file_utils" require "yaml" # A funko, built from its source metadata @@ -10,23 +12,30 @@ class Funko # if Nil, it has no template whatsoever property runtime : (String | Nil)? = nil - # Port of the funko process (optional, default is 3000) - property port : UInt32? = 3000 + # Extra packages shipped with the Docker image + property ship_packages : Array(String) = [] of String - # Extra packages, passed as EXTRA_PACKAGES argument - # to the Dockerfile, use it for installing things in - # the SHIPPED docker image - property extra_packages : Array(String)? - - # Extra packages, passed as DEVEL_PACKAGES argument - # to the Dockerfile, use it for installing things in - # the docker stage used to build the code - property devel_packages : Array(String)? + # Extra packages used only in the *build* image + property devel_packages : Array(String) = [] of String # Where this is located in the filesystem @[YAML::Field(ignore: true)] property path : String = "" + # Healthcheck properties + property healthcheck_options : String = "--interval=1m --timeout=2s --start-period=2s --retries=3" + property healthcheck_command : String = "curl --fail http://localhost:3000/ping || exit 1" + + def _to_context + { + "name" => name, + "ship_packages" => ship_packages, + "devel_packages" => devel_packages, + "healthcheck_options" => healthcheck_options, + "healthcheck_command" => healthcheck_command, + } + end + # Create an Array of funkos from an Array of folders containing definitions def self.from_paths(paths : Array(String | Path)) : Array(Funko) paths.map { |path| Path.new(path, "funko.yml") } @@ -38,6 +47,42 @@ class Funko } end + # Setup the target directory `path` with all the files needed + # to build a docker image + def prepare_build(path : Path) + # Copy runtime if requested + if !runtime.nil? + runtime_dir = Path.new("runtimes", runtime.as(String)) + raise Exception.new("Error: runtime #{runtime} not found for funko #{name} in #{path}") unless File.exists?(runtime_dir) + Dir.glob("#{runtime_dir}/*").each { |src| + FileUtils.cp_r(src, path) + } + # Replace templates with processed files + context = _to_context + Dir.glob("#{path}/**/*.j2").each { |template| + dst = template[..-4] + File.open(dst, "w") do |file| + file << Crinja.render(File.read(template), context) + end + File.delete template + } + end + + # Copy funko + raise Exception.new("Internal error: empty funko path for #{name}") if self.path.empty? + Dir.glob("#{self.path}/*").each { |src| + FileUtils.cp_r(src, path) + } + end + + # Build image using docker in path previously prepared using `prepare_build` + def build(path : Path) + docker_api = Docr::API.new(Docr::Client.new) + docker_api.images.build( + context: path.to_s, + tags: ["#{name}:latest"]) { } + end + # Return a list of image IDs for this funko, most recent first def image_history docker_api = Docr::API.new(Docr::Client.new) @@ -112,15 +157,8 @@ class Funko image: "#{name}:latest", hostname: name, # Port in the container side - # FIXME: Maybe don't need this now we are using the proxy - exposed_ports: {"#{port}/tcp" => {} of String => String}, host_config: Docr::Types::HostConfig.new( network_mode: "faaso-net", - # Also probably not needed anymore - port_bindings: {"#{port}/tcp" => [Docr::Types::PortBinding.new( - host_port: "", # Host port, empty means random - host_ip: "127.0.0.1", # Host IP - )]} ) )