# apisix — 업스트림 apache/apisix:3.17.0-debian 를 대체하는 자체 빌드. # # 업스트림은 vanilla OpenResty 가 아니라 "APISIX-Runtime" 이라는 커스텀 컴파일 nginx다 # (openssl3·zlib·pcre 를 직접 빌드해 넣고, apisix-nginx-module·wasm-nginx-module(WASM, # wasmtime)·lua-var-nginx-module·lua-resty-events·mod_dubbo·ngx_multi_upstream_module # 를 --add-module 로 정적으로 얹는다 — 2026-08-11 `openresty -V` 로 실측). 업스트림 빌드 # 스크립트(https://github.com/api7/apisix-build-tools, 태그 apisix-runtime/1.3.6 — # `openresty -V` 의 APISIX_RUNTIME_VER=1.3.6 과 실측 일치)가 배포판 무관하게 소스에서 # 컴파일하는 구조라 SUSE BCI 로 옮길 수 있었다 — Debian/RHEL 전용 사전빌드 RPM/DEB 를 # SUSE 에 강제 설치하는 건 ABI 가 안 맞아 불가능하다(dockerfiles/Dockerfile.apisix.rpm # 등 참고, 둘 다 UBI9/Ubuntu 전용). # # 왜 자체 빌드인가 — apache/apisix:3.17.0-debian(2026-08-06 배포, 확인 시점 최신 태그)이 # 게이트 차단 26건. 이미 최신 태그라 상위 태그 교체 불가, Debian 베이스 OS 패키지 # (libc/perl/pcre 등) CVE 라 이번엔 정적 링크 바이너리가 아니라 진짜 베이스 OS 문제다. # # 구성(3단계): # 1) runtime — OpenSSL 3.4.1·zlib·pcre 를 $OR_PREFIX/{openssl3,zlib,pcre} 에 직접 # 빌드하고, openresty-${OR_VER} 소스에 위 커스텀 모듈들을 --add-module 로 붙여 # 컴파일한다(업스트림 빌드 스크립트 그대로 재현, 버전 전부 pinned). # 2) apisix — runtime 위에 APISIX 본체(Lua 애플리케이션 + 일부 C 확장 Lua rock)를 # luarocks 로 설치한다. 이 단계에서 필요한 pcre2·openldap·libxml2·libxslt(lua-resty- # saml 이 xmlsec1 바인딩에 씀)·zlib devel 은 SUSE 표준 패키지로 설치한다(업스트림처럼 # openresty 전용 -devel 패키지가 아니라 시스템 표준 -devel — 실제 배포판 표준 # pcre/pcre2/libxml2/libxslt 헤더로 컴파일해도 무방한 부분이라 문제 없다). # 3) final — bci-base(런타임 공유 라이브러리 필요: libxml2·libxslt·openldap2 등을 # 정적이 아니라 동적 링크로 쓰는 Lua rock 이 있어 bci-micro 로는 부족하다 — 실측 # 후 bci-micro 로 축소 검토, 1차는 정확성 우선). # # 업스트림과 다르게 하는 부분 # - 베이스: Debian → SUSE BCI(.claude/image-authoring.md 원칙 2). # - Rust 툴체인: rustup 으로 설치(wasm-nginx-module 의 wasmtime-c-api, APISIX 일부 # rock 컴파일에 필요 — 업스트림도 동일하게 rustup 을 쓴다, 배포판 무관 설치라 차이 없음). # - 그 외 애플리케이션 코드·모듈 버전은 100% 동일(diff 최소화 원칙). # syntax=docker/dockerfile:1 ARG BUILDER_BASE=registry.suse.com/bci/bci-base:15.7 ARG RUNTIME_BASE=registry.suse.com/bci/bci-base:15.7 # ============================================================================= # 1) runtime — OpenSSL/zlib/pcre + OpenResty(APISIX-Runtime 커스텀 모듈 전체) # ============================================================================= FROM ${BUILDER_BASE} AS runtime ARG OPENRESTY_VERSION=1.29.2.4 ARG OPENSSL_VERSION=3.4.1 ARG APISIX_NGINX_MODULE_VER=1.19.5 ARG WASM_NGINX_MODULE_VER=0.7.0 ARG LUA_VAR_NGINX_MODULE_VER=v0.5.3 ARG LUA_RESTY_EVENTS_VER=0.2.0 ARG NGX_MULTI_UPSTREAM_MODULE_VER=1.3.3 ARG MOD_DUBBO_VER=1.0.2 ARG APISIX_RUNTIME_VER=1.3.6 ENV OR_PREFIX=/usr/local/openresty ENV PATH=/root/.cargo/bin:$PATH RUN zypper -n refresh && zypper -n install -y \ gcc gcc-c++ make patch git wget curl tar gzip xz which findutils perl unzip gawk # 업스트림처럼 cpanm 으로 IPC::Cmd 를 보강한다(OpenSSL 3.x Configure 가 요구) — SUSE 는 # perl-App-cpanminus 패키지가 없어 공식 부트스트랩으로 설치. RUN curl -L https://cpanmin.us | perl - App::cpanminus \ && cpanm --notest IPC::Cmd RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y WORKDIR /tmp/build # --- zlib 먼저 (업스트림 openresty-zlib-devel 대신 표준 소스 빌드 — 같은 산출 경로). # OpenSSL 의 `zlib` config 옵션이 컴파일 타임에 zlib.h 를 요구하므로 OpenSSL 보다 # 먼저 빌드해야 한다 — 실제로 순서를 반대로 뒀다가 `zlib.h: No such file` 로 실패한 # 것을 실측했다(2026-08-11). ARG ZLIB_VERSION=1.3.1 RUN wget "https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz" \ && tar xzf "zlib-${ZLIB_VERSION}.tar.gz" \ && cd "zlib-${ZLIB_VERSION}" \ && ./configure --prefix="${OR_PREFIX}/zlib" \ && make -j"$(nproc)" \ && make install # --- pcre (classic PCRE1, nginx/openresty 기본 요구사항 — 업스트림 openresty-pcre-devel # 과 같은 산출 경로 $OR_PREFIX/pcre 에 설치) --- ARG PCRE_VERSION=8.45 RUN wget "https://sourceforge.net/projects/pcre/files/pcre/${PCRE_VERSION}/pcre-${PCRE_VERSION}.tar.gz/download" -O "pcre-${PCRE_VERSION}.tar.gz" \ && tar xzf "pcre-${PCRE_VERSION}.tar.gz" \ && cd "pcre-${PCRE_VERSION}" \ && ./configure --prefix="${OR_PREFIX}/pcre" --enable-jit --enable-utf --enable-unicode-properties \ && make -j"$(nproc)" \ && make install # --- OpenSSL 3.4.1 (업스트림과 동일 버전) — zlib 헤더 경로를 명시로 넘긴다 --- RUN wget --no-check-certificate "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz" \ && tar xzf "openssl-${OPENSSL_VERSION}.tar.gz" \ && cd "openssl-${OPENSSL_VERSION}" \ && CFLAGS="-I${OR_PREFIX}/zlib/include" LDFLAGS="-L${OR_PREFIX}/zlib/lib -Wl,-rpath,${OR_PREFIX}/zlib/lib" \ ./config shared zlib enable-camellia enable-seed enable-rfc3779 \ enable-cms enable-md2 enable-rc5 enable-weak-ssl-ciphers \ --prefix="${OR_PREFIX}/openssl3" --libdir=lib \ --with-zlib-lib="${OR_PREFIX}/zlib/lib" --with-zlib-include="${OR_PREFIX}/zlib/include" \ && make -j"$(nproc)" \ && make install_sw install_ssldirs # --- OpenResty 소스 + 커스텀 모듈 (업스트림 build-apisix-runtime.sh 재현) --- RUN wget --no-check-certificate "https://openresty.org/download/openresty-${OPENRESTY_VERSION}.tar.gz" \ && tar zxf "openresty-${OPENRESTY_VERSION}.tar.gz" RUN git clone --depth=1 -b "${LUA_RESTY_EVENTS_VER}" https://github.com/Kong/lua-resty-events.git "lua-resty-events-${LUA_RESTY_EVENTS_VER}" \ && git clone --depth=1 -b "${NGX_MULTI_UPSTREAM_MODULE_VER}" https://github.com/api7/ngx_multi_upstream_module.git "ngx_multi_upstream_module-${NGX_MULTI_UPSTREAM_MODULE_VER}" \ && git clone --depth=1 -b "${MOD_DUBBO_VER}" https://github.com/api7/mod_dubbo.git "mod_dubbo-${MOD_DUBBO_VER}" \ && git clone --depth=1 -b "${APISIX_NGINX_MODULE_VER}" -- https://github.com/api7/apisix-nginx-module.git "apisix-nginx-module-${APISIX_NGINX_MODULE_VER}" \ && git clone --depth=1 -b "${WASM_NGINX_MODULE_VER}" https://github.com/api7/wasm-nginx-module.git "wasm-nginx-module-${WASM_NGINX_MODULE_VER}" \ && git clone --depth=1 -b "${LUA_VAR_NGINX_MODULE_VER}" https://github.com/api7/lua-var-nginx-module "lua-var-nginx-module-${LUA_VAR_NGINX_MODULE_VER}" RUN cd "ngx_multi_upstream_module-${NGX_MULTI_UPSTREAM_MODULE_VER}" && ./patch.sh "../openresty-${OPENRESTY_VERSION}" && cd .. \ && cd "apisix-nginx-module-${APISIX_NGINX_MODULE_VER}/patch" && ./patch.sh "../../openresty-${OPENRESTY_VERSION}" && cd ../.. \ && cd "wasm-nginx-module-${WASM_NGINX_MODULE_VER}" && ./install-wasmtime.sh && cd .. RUN cd "openresty-${OPENRESTY_VERSION}" \ && or_limit_ver=0.09 \ && rm -rf "bundle/lua-resty-limit-traffic-${or_limit_ver}" \ && limit_ver=1.2.0 \ && wget "https://github.com/api7/lua-resty-limit-traffic/archive/refs/tags/v${limit_ver}.tar.gz" -O "lua-resty-limit-traffic-${limit_ver}.tar.gz" \ && tar xzf "lua-resty-limit-traffic-${limit_ver}.tar.gz" \ && mv "lua-resty-limit-traffic-${limit_ver}" "bundle/lua-resty-limit-traffic-${or_limit_ver}" RUN cd "openresty-${OPENRESTY_VERSION}" \ && zlib_prefix="${OR_PREFIX}/zlib" pcre_prefix="${OR_PREFIX}/pcre" openssl_prefix="${OR_PREFIX}/openssl3" ; \ ./configure --prefix="${OR_PREFIX}" \ --with-cc-opt="-DAPISIX_RUNTIME_VER=${APISIX_RUNTIME_VER} -DNGX_LUA_ABORT_AT_PANIC -I${zlib_prefix}/include -I${pcre_prefix}/include -I${openssl_prefix}/include" \ --with-ld-opt="-Wl,-rpath,${OR_PREFIX}/wasmtime-c-api/lib -L${zlib_prefix}/lib -L${pcre_prefix}/lib -L${openssl_prefix}/lib -Wl,-rpath,${zlib_prefix}/lib:${pcre_prefix}/lib:${openssl_prefix}/lib" \ --add-module="../mod_dubbo-${MOD_DUBBO_VER}" \ --add-module="../ngx_multi_upstream_module-${NGX_MULTI_UPSTREAM_MODULE_VER}" \ --add-module="../apisix-nginx-module-${APISIX_NGINX_MODULE_VER}" \ --add-module="../apisix-nginx-module-${APISIX_NGINX_MODULE_VER}/src/stream" \ --add-module="../apisix-nginx-module-${APISIX_NGINX_MODULE_VER}/src/meta" \ --add-module="../wasm-nginx-module-${WASM_NGINX_MODULE_VER}" \ --add-module="../lua-var-nginx-module-${LUA_VAR_NGINX_MODULE_VER}" \ --add-module="../lua-resty-events-${LUA_RESTY_EVENTS_VER}" \ --with-poll_module --with-pcre-jit \ --without-http_rds_json_module --without-http_rds_csv_module --without-lua_rds_parser \ --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module \ --with-http_v2_module --with-http_v3_module \ --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module \ --with-http_stub_status_module --with-http_realip_module --with-http_addition_module \ --with-http_auth_request_module --with-http_secure_link_module --with-http_random_index_module \ --with-http_gzip_static_module --with-http_sub_module --with-http_dav_module \ --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module \ --with-threads --with-compat \ --with-luajit-xcflags="-DLUAJIT_NUMMODE=2 -DLUAJIT_ENABLE_LUA52COMPAT" \ -j"$(nproc)" \ && make -j"$(nproc)" \ && make install RUN cd "lua-resty-events-${LUA_RESTY_EVENTS_VER}" \ && install -d "${OR_PREFIX}/lualib/resty/events/" \ && install -m 664 lualib/resty/events/*.lua "${OR_PREFIX}/lualib/resty/events/" \ && install -d "${OR_PREFIX}/lualib/resty/events/compat/" \ && install -m 644 lualib/resty/events/compat/*.lua "${OR_PREFIX}/lualib/resty/events/compat/" RUN cd "apisix-nginx-module-${APISIX_NGINX_MODULE_VER}" && OPENRESTY_PREFIX="${OR_PREFIX}" make install \ && cd "../wasm-nginx-module-${WASM_NGINX_MODULE_VER}" && OPENRESTY_PREFIX="${OR_PREFIX}" make install # ============================================================================= # 2) apisix — APISIX 본체(Lua 애플리케이션) 설치 # ============================================================================= FROM runtime AS apisix-app ARG APISIX_VERSION=3.17.0 ENV PATH=$PATH:${OR_PREFIX}/luajit/bin:${OR_PREFIX}/nginx/sbin:${OR_PREFIX}/bin # lua-resty-saml(xmlsec1 바인딩)·lyaml·기타 rock 컴파일에 필요. 업스트림은 openresty # 전용 -devel 대신 시스템 표준 pcre/pcre2/openldap/libxml2/libxslt/zlib/yaml -devel 를 # 쓴다(RHEL 기준 utils/install-dependencies.sh 참고) — SUSE 표준 패키지로 대응한다. # sudo 는 utils/linux-install-luarocks.sh 내부에서 그대로 호출한다(스크립트 수정 없이 # 재사용하려고 패키지로 설치 — 이 스테이지는 어차피 root 로 실행된다). RUN zypper -n install -y \ pcre-devel pcre2-devel openldap2-devel \ libxml2-devel libxslt-devel zlib-devel libyaml-devel \ diffutils cmake automake autoconf libtool gawk readline-devel sudo RUN wget https://raw.githubusercontent.com/apache/apisix/${APISIX_VERSION}/utils/linux-install-luarocks.sh \ && chmod +x linux-install-luarocks.sh \ && ./linux-install-luarocks.sh WORKDIR /apisix RUN git clone --depth=1 -b "${APISIX_VERSION}" https://github.com/apache/apisix.git . # apisix-master-0.rockspec 는 저장소 루트에 있다(2026-08-11 태그 3.17.0 기준 실측). # `luarocks make` 는 rockspec 의 source.url(원격 tarball)을 쓰지 않고 현재 디렉토리를 # 그대로 빌드 대상으로 삼는다 — git clone 만으로 충분하고 apiseven 빌드처럼 source.url # 을 로컬 경로로 sed 패치할 필요가 없다. # # lua-resty-saml(luarocks 가 자동으로 받는 의존 rock)의 xmlsec 바인딩(src/saml.c)이 # libxml2 2.12(SUSE 표준 -devel 버전) 의 `xmlSetStructuredErrorFunc` 시그니처(콜백 인자에 # const 추가됨)와 안 맞아 `-Werror=incompatible-pointer-types` 로 빌드 실패한다(2026-08-11 # 실측) — rock 자체의 Makefile 이 `CFLAGS_ALL := ... -Werror ...` 를 하드코딩해 환경변수 # CFLAGS 로는 못 끈다. rock 소스를 포크하지 않고, 이 스테이지 안에서만 쓰는 gcc 래퍼로 # 그 진단 하나만 경고로 낮춘다(뒤에 오는 -Wno-error=가 앞의 -Werror 를 그 진단에 한해 이긴다). RUN mv /usr/bin/gcc /usr/bin/gcc.real \ && printf '#!/bin/sh\nexec /usr/bin/gcc.real -Wno-error=incompatible-pointer-types "$@"\n' > /usr/bin/gcc \ && chmod +x /usr/bin/gcc RUN luarocks make ./apisix-master-0.rockspec --tree=/usr/local/apisix/deps --local # 이후 단계에 영향 주지 않도록 원복한다 — 위 gcc 래퍼는 lua-resty-saml 빌드에만 쓴다. RUN mv /usr/bin/gcc.real /usr/bin/gcc # apisix-build-tools 의 install_apisix() 를 재현한다(utils/install-common.sh) — luarocks # 가 rockspec 의 build.install.bin 으로 설치한 bin/apisix 래퍼와, deps 트리 안에 설치된 # apisix Lua 패키지(share/lua/5.1/apisix)를 /usr/local/apisix 구조로 재배치한다. # # ui/ 는 apache/apisix 저장소 자체엔 없다(2026-08-11 3.17.0 태그 실측) — apiseven 의 # 패키징 파이프라인이 별도로 admin 대시보드 프론트엔드를 빌드해 끼워 넣는 단계라 # (Node.js/yarn 빌드, Dockerfile.package.apisix), 소스만 git clone 해서는 재현할 수 # 없다. 우리 apisix 카탈로그 배포는 이 내장 UI 를 쓰지 않는다(custom-values.yaml 에 # 관련 설정 없음) — 없으면 빈 디렉터리만 만들고 건너뛴다(있으면 그대로 복사). RUN mkdir -p /usr/local/apisix \ && cp -r conf /usr/local/apisix/conf \ && { cp -r ui /usr/local/apisix/ui 2>/dev/null || mkdir -p /usr/local/apisix/ui; } \ && install -m 755 bin/apisix /usr/local/apisix/apisix-cli-bin \ && mv /usr/local/apisix/deps/share/lua/5.1/apisix /usr/local/apisix/apisix \ && sed -i '1i package.path = "/usr/local/apisix/deps/share/lua/5.1/?/init.lua;" .. package.path' \ /usr/local/apisix/apisix/cli/apisix.lua # ============================================================================= # 3) final # ============================================================================= FROM ${RUNTIME_BASE} AS final # 실제 SUSE 공유 라이브러리 패키지명은 soname 이 붙는다(libxml2/libldap 등은 이미 # bci-base 기본 설치에 있어 명시 안 해도 되지만, 명확성을 위해 실측한 이름 그대로 적는다 # — 2026-08-11 zypper search 로 확인: libxml2-2, libpcre2-8-0, libldap-2_4-2 는 기본 # 포함, libpcre1·libxslt1·libyaml-0-2 만 추가 설치가 필요했다). # gawk 는 라이브러리가 아니라 /usr/bin/apisix 래퍼 스크립트 자체가 openresty 버전 # 파싱에 awk 를 쓰기 때문에 필요하다(2026-08-11 실측: "awk: command not found"). RUN zypper -n install -y libpcre1 libxslt1 libyaml-0-2 libxml2-2 libpcre2-8-0 gawk \ && zypper -n clean --all COPY --from=apisix-app /usr/local/openresty /usr/local/openresty COPY --from=apisix-app /usr/local/apisix /usr/local/apisix COPY --from=apisix-app /usr/local/apisix/apisix-cli-bin /usr/bin/apisix ENV PATH=$PATH:/usr/local/openresty/luajit/bin:/usr/local/openresty/nginx/sbin:/usr/local/openresty/bin RUN chmod 755 /usr/bin/apisix \ && rm -f /usr/local/apisix/apisix-cli-bin \ && mkdir -p /usr/local/apisix/logs /usr/local/apisix/conf/cert \ && groupadd --system --gid 636 apisix \ && useradd --system --gid apisix --no-create-home --shell /usr/sbin/nologin --uid 636 apisix \ && chown -R apisix:0 /usr/local/apisix \ && chmod -R g=u /usr/local/apisix \ && ln -sf /dev/stdout /usr/local/apisix/logs/access.log \ && ln -sf /dev/stderr /usr/local/apisix/logs/error.log # keycloak-authz 커스텀 플러그인 (paasup/dataup 레포의 dockerfile 과 동일한 오버레이) COPY keycloak-authz.lua /usr/local/apisix/apisix/plugins/keycloak-authz.lua COPY docker-entrypoint.sh /docker-entrypoint.sh RUN chmod 755 /docker-entrypoint.sh WORKDIR /usr/local/apisix USER apisix EXPOSE 9080 9443 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["docker-start"]