{"id":304,"date":"2021-11-30T16:35:00","date_gmt":"2021-11-30T08:35:00","guid":{"rendered":"https:\/\/blog.swineson.me\/en\/?p=304"},"modified":"2022-03-31T15:37:16","modified_gmt":"2022-03-31T07:37:16","slug":"gitlab-ci-runner-on-rootless-podman","status":"publish","type":"post","link":"https:\/\/blog.swineson.me\/en\/gitlab-ci-runner-on-rootless-podman\/","title":{"rendered":"GitLab CI Runner on Rootless Podman"},"content":{"rendered":"<p>GitLab CI runner can be contained in a completely rootless environment. It can start as a non-root user, and work with a rootless Podman instance as a Docker runner. And here is how I achieved it.<\/p>\n<p>My CI host configuration:<\/p>\n<ul>\n<li>Ubuntu 20.04<\/li>\n<li>Podman 3.4.2<\/li>\n<li>GitLab Runner 14.5.0<\/li>\n<\/ul>\n<p>A similar procedure can be applied to other distros as well.<\/p>\n<p><!--more--><\/p>\n<h2>Install Podman<\/h2>\n<p>Use the following script for installing the latest version of Podman onto a Ubuntu.<\/p>\n<pre class=\"lang:sh decode:true\">. \/etc\/os-release\r\necho \"deb https:\/\/download.opensuse.org\/repositories\/devel:\/kubic:\/libcontainers:\/stable\/xUbuntu_${VERSION_ID}\/ \/\" | sudo tee \/etc\/apt\/sources.list.d\/devel:kubic:libcontainers:stable.list\r\ncurl -L \"https:\/\/download.opensuse.org\/repositories\/devel:\/kubic:\/libcontainers:\/stable\/xUbuntu_${VERSION_ID}\/Release.key\" | sudo apt-key add -\r\nsudo apt-get update\r\nsudo apt-get -y upgrade\r\nsudo apt-get -y install podman<\/pre>\n<p>For other distros, please refer to <a href=\"https:\/\/podman.io\/getting-started\/installation\" target=\"_blank\" rel=\"noopener\">the official documentation<\/a>.<\/p>\n<h2>Install GitLab Runner<\/h2>\n<pre class=\"lang:sh decode:true\">curl -L \"https:\/\/packages.gitlab.com\/install\/repositories\/runner\/gitlab-runner\/script.deb.sh\" | sudo bash\r\nsudo apt-get install gitlab-runner strace\r\n\r\nchown -R root:gitlab-runner \/etc\/gitlab-runner\r\nchmod 750 \/etc\/gitlab-runner\r\nchmod 640 \/etc\/gitlab-runner\/config.toml<\/pre>\n<p>For other distros, please refer to <a href=\"https:\/\/docs.gitlab.com\/runner\/install\/linux-repository.html\" target=\"_blank\" rel=\"noopener\">the official documentation<\/a>.<\/p>\n<p>Note that we installed strace here, which we will be using later.<\/p>\n<h2>Config User Namespace Mapping<\/h2>\n<p>You need to allocate a range of UID\/GIDs per user. You might adjust them for your needs.<\/p>\n<pre class=\"lang:sh decode:true\">sudo usermod --add-subuids 200000-265536 --add-subgids 200000-265536 gitlab-runner\r\n\r\ncat &gt; \/etc\/sysctl.d\/90-podman.conf &lt;&lt;EOF\r\nkernel.unprivileged_userns_clone=1\r\nEOF\r\nsysctl -f \/etc\/sysctl.d\/90-podman.conf<\/pre>\n<h2>Enable Podman Service<\/h2>\n<pre class=\"lang:sh decode:true\">loginctl enable-linger gitlab-runner\r\n\r\nmkdir -p ~gitlab-runner\/.config\/systemd\/user\/sockets.target.wants ~gitlab-runner\/.config\/systemd\/user\/multi-user.target.wants\r\nchown gitlab-runner:gitlab-runner -R ~gitlab-runner\/.config\/systemd\/user\/*\r\nXDG_RUNTIME_DIR=\"\/run\/user\/$(id -u gitlab-runner)\" sudo -Eu gitlab-runner -- systemctl --user daemon-reload\r\nXDG_RUNTIME_DIR=\"\/run\/user\/$(id -u gitlab-runner)\" sudo -Eu gitlab-runner -- systemctl --user enable --now podman.socket podman.service\r\nsudo -u gitlab-runner -- podman network create podman<\/pre>\n<p>Then we need to verify if Podman is enabled correctly:<\/p>\n<pre class=\"lang:sh decode:true\">sudo su - gitlab-runner bash -c 'podman system info'\r\nsudo su - gitlab-runner bash -c 'podman run --rm hello-world'<\/pre>\n<h2>Enable GitLab Runner Service<\/h2>\n<pre class=\"lang:sh decode:true\">systemctl stop gitlab-runner\r\nmkdir -p \/etc\/systemd\/system\/gitlab-runner.service.d\r\ncat &gt; \/etc\/systemd\/system\/gitlab-runner.service.d\/override.conf &lt;&lt;EOF\r\n[Service]\r\nUser=gitlab-runner\r\nGroup=gitlab-runner\r\nExecStart=\r\nExecStart=strace -e getuid,getgid -e inject=getuid:retval=0 -e inject=getgid:retval=0 -- \/usr\/bin\/gitlab-runner run --working-directory \/home\/gitlab-runner --config \/etc\/gitlab-runner\/config.toml --service gitlab-runner --user gitlab-runner\r\n\r\nEOF\r\nsystemctl daemon-reload\r\nsystemctl enable --now gitlab-runner<\/pre>\n<p>Run <span class=\"lang:sh highlight:0 decode:true crayon-inline \">gitlab-runner register<\/span> as usual. After registration, set <span class=\"lang:ini highlight:0 decode:true crayon-inline \">runners[].docker.host = &#8220;unix:\/\/\/run\/user\/996\/podman\/podman.sock&#8221;<\/span> , then restart the runner service. (<span class=\"lang:default highlight:0 decode:true crayon-inline \">996<\/span>\u00a0 is the UID for <span class=\"lang:default highlight:0 decode:true crayon-inline\">gitlab-runner<\/span> user. It might differ in your environment. )<\/p>\n<p>Note: the weird <span class=\"lang:sh highlight:0 decode:true crayon-inline \">strace<\/span> command <a href=\"https:\/\/gitlab.com\/gitlab-org\/gitlab-runner\/-\/blob\/7e20073a816f1912bca86f57b6903c2af9c8b307\/commands\/user_mode_warning.go#L21\" target=\"_blank\" rel=\"noopener\">tricks GitLab runner into thinking it is started by the root user<\/a>, so it launches into system mode. By default, the runner will be started in user mode which <a href=\"https:\/\/stackoverflow.com\/a\/62436486\/2646069\" target=\"_blank\" rel=\"noopener\">is not helpful in our use case<\/a>.<\/p>\n<h1>Known Issues<\/h1>\n<h2>CentOS 6 containers does not work<\/h2>\n<p>If you need to run CentOS 6 or other similar old Docker containers, you need to append <span class=\"lang:default highlight:0 decode:true crayon-inline \">vsyscall=emulate<\/span>\u00a0 to the kernel command line and reboot.<\/p>\n<h2>Not able to pull container using short names<\/h2>\n<pre class=\"lang:sh decode:true \">cat &gt; \/etc\/containers\/registries.conf &lt;&lt;EOF\r\nunqualified-search-registries = [\"docker.io\"]\r\nEOF<\/pre>\n<h2>Using Self-signed CA<\/h2>\n<p>For GitLab runner to connect to an HTTPS endpoint, you only need to install the CA to the system-level certificate storage, then change <span class=\"lang:ini highlight:0 decode:true crayon-inline \">runners[].url<\/span> to a HTTPS URL.<\/p>\n<p>For Git to successfully pull repos using HTTPS, you have to first modify <a href=\"https:\/\/hub.docker.com\/r\/gitlab\/gitlab-runner-helper\" target=\"_blank\" rel=\"noopener\">the official helper images<\/a>, then set <span class=\"lang:ini highlight:0 decode:true crayon-inline \">runners[].docker.helper_image = &#8220;my.registry.local\/gitlab\/gitlab-runner-helper:x86_64-v${CI_RUNNER_VERSION}&#8221;<\/span> . If you really don&#8217;t care about security, set\u00a0 <span class=\"lang:ini highlight:0 decode:true crayon-inline\">runners[].docker.environment = [&#8220;GIT_SSL_NO_VERIFY=true&#8221;]<\/span> (very insecure, very dangerous).<\/p>\n<p>For the actual CI workload to connect to HTTPS sites with self-signed CA, set <span class=\"lang:ini highlight:0 decode:true crayon-inline \">runners[].docker.volumes = [&#8220;\/cache&#8221;, &#8220;\/usr\/local\/share\/ca-certificates:\/usr\/local\/share\/ca-certificates:ro&#8221;, &#8220;\/etc\/ssl\/certs:\/etc\/ssl\/certs:ro&#8221;]<\/span> . This might not work if your container or host OS doesn&#8217;t follow the conventions.<\/p>\n<h2>Changed subuid\/subgid mapping, need to reset podman<\/h2>\n<p>If you changed subuid\/subgid mapping or did not set it up correctly initially, Podman might act weird. Run the following commands to resolve (and you need to manually fix any others errors displayed when running these commands). Note the commands will completely wipe every bit of data (container, image, volumes) stored in your Podman database.<\/p>\n<pre class=\"lang:sh decode:true \">USERNAME=\"gitlab-runner\"\r\nUSER_UID=$(id -u \"$USERNAME\")\r\nrm -rf ~\"$USERNAME\"\/.{config,local\/share}\/containers \/run\/user\/\"$USER_UID\"\/{libpod,runc,vfs-*}\r\nsu - \"$USERNAME\" systemctl --user restart dbus\r\nsu - \"$USERNAME\" podman system reset\r\nsu - \"$USERNAME\" podman system migrate<\/pre>\n<h2>Unable to upgrade to tcp, received 409<\/h2>\n<p>You might receive the following error:<\/p>\n<pre class=\"lang:default highlight:0 decode:true \">ERROR: Job failed (system failure): prepare environment: unable to upgrade to tcp, received 409 (exec.go:59:0s). Check https:\/\/docs.gitlab.com\/runner\/shells\/index.html#shell-profile-loading for more information<\/pre>\n<p>Check your file permissions. Try disable the cache configuration on the runner and try again. The outermost cache directory might need a permission of <span class=\"lang:default highlight:0 decode:true crayon-inline \">0755<\/span>\u00a0.<\/p>\n<h2>CNI network &#8220;podman&#8221; not found<\/h2>\n<p>Run <span class=\"lang:sh decode:true crayon-inline \">sudo -u gitlab-runner &#8212; podman network create podman<\/span> .<\/p>\n<h2>Generic Debugging Advices<\/h2>\n<p>To enable verbose mode on the GitLab runner, you can either add <span class=\"lang:default highlight:0 decode:true crayon-inline \">log_level = &#8220;debug&#8221;<\/span> on the top level of the config file, or use <span class=\"lang:sh highlight:0 decode:true crayon-inline \">gitlab-runner &#8211;debug run -c \/etc\/gitlab-runner\/config.toml<\/span> to launch a temporary runner.<\/p>\n<p>To debug Docker API errors, you can use BPF to capture traffic on the UNIX socket, or convert the socket to a TCP listener with <span class=\"lang:sh decode:true crayon-inline \">socat TCP-LISTEN:12345,reuseaddr,fork UNIX-CLIENT:\/run\/user\/996\/podman\/podman.sock<\/span> , use <span class=\"lang:ini decode:true crayon-inline \">host = &#8220;tcp:\/\/127.0.0.1:12345&#8221;<\/span> in the runner config to connect, then use <span class=\"lang:sh decode:true crayon-inline \">tcpdump -i lo -w docker.pcap tcp port 12345<\/span> to see what happened.<\/p>\n<h1>Bonus: Use tmpfs to run the containers<\/h1>\n<p>This will clear all your previous podman data, so make sure you back up important images and containers.<\/p>\n<pre class=\"lang:sh decode:true\">mkdir -p \/mnt\/gitlab-runner-volatile\r\necho \"tmpfs \/mnt\/gitlab-runner-volatile tmpfs defaults,noatime,size=64g,uid=996,gid=996,mode=0755,huge=always\" &gt;&gt; \/etc\/fstab\r\nmount \/mnt\/gitlab-runner-volatile\r\n\r\nsu - gitlab-runner\r\npodman system reset\r\ncat &gt; ~gitlab-runner\/.config\/containers\/storage.conf &lt;&lt;EOF\r\n[storage]\r\ndriver = \"overlay\"\r\nrunroot = \"\/mnt\/gitlab-runner-volatile\/podman_containers\/runroot\"\r\ngraphroot = \"\/mnt\/gitlab-runner-volatile\/podman_containers\/graphroot\"\r\nrootless_storage_path = \"\/mnt\/gitlab-runner-volatile\/podman_containers\/storage\"\r\n\r\n[storage.options.overlay]\r\nmountopt = \"nodev,metacopy=on\"\r\nmount_program = \"\/usr\/bin\/fuse-overlayfs\"\r\nEOF\r\n\r\nXDG_RUNTIME_DIR=\/run\/user\/$(id -u gitlab-runner) systemctl --user restart podman.service<\/pre>\n<p><a href=\"https:\/\/ahelpme.com\/software\/podman\/change-the-location-of-container-storage-in-podman-with-selinux-enabled\/\" target=\"_blank\" rel=\"noopener\">Additional configuration required for SELinux-enabled hosts.<\/a><\/p>\n<hr \/>\n<h1>References<\/h1>\n<ul>\n<li><a href=\"https:\/\/access.redhat.com\/documentation\/zh-cn\/red_hat_enterprise_linux\/8\/html\/building_running_and_managing_containers\/using-the-container-tools-api_using-the-container-tools-cli\" target=\"_blank\" rel=\"noopener\">\u4f7f\u7528 CONTAINER-TOOLS API<\/a><\/li>\n<li><a href=\"https:\/\/unix.stackexchange.com\/questions\/251211\/why-doesnt-my-systemd-user-unit-start-at-boot\" target=\"_blank\" rel=\"noopener\">Why doesn&#8217;t my systemd user unit start at boot?<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/containers\/podman\/issues\/9280\" target=\"_blank\" rel=\"noopener\">rootless service not work with systemd socket activation<\/a><\/li>\n<li><a href=\"https:\/\/gitlab.com\/gitlab-org\/gitlab-runner\/-\/issues\/27119\" target=\"_blank\" rel=\"noopener\">Add support for Podman to GitLab Runner<\/a><\/li>\n<li><a href=\"https:\/\/www.redhat.com\/sysadmin\/container-networking-podman\" target=\"_blank\" rel=\"noopener\">Configuring container networking with Podman<\/a><\/li>\n<li><a href=\"https:\/\/serverfault.com\/questions\/517906\/how-to-expose-a-unix-domain-socket-directly-over-tcp\" target=\"_blank\" rel=\"noopener\">How to expose a UNIX domain socket directly over TCP<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/containers\/podman\/issues\/8772\" target=\"_blank\" rel=\"noopener\">Can&#8217;t use tmpfs as runroot for containers<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>GitLab CI runner can be contained in a completely rootless environment. It can start as a non-root user, and work with a rootless Podman instance as a Docker runner. And here is how I achieved it. My CI host configuration: Ubuntu 20.04 Podman 3.4.2 GitLab Runner 14.5.0 A similar procedure can be applied to other [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[],"class_list":["post-304","post","type-post","status-publish","format-standard","hentry","category-linux"],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/posts\/304"}],"collection":[{"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/comments?post=304"}],"version-history":[{"count":19,"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/posts\/304\/revisions"}],"predecessor-version":[{"id":329,"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/posts\/304\/revisions\/329"}],"wp:attachment":[{"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/media?parent=304"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/categories?post=304"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.swineson.me\/en\/wp-json\/wp\/v2\/tags?post=304"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}