Containers Cheat Sheet
Docker
Install Docker (moby) on Ubuntu 22.04 with GitHub Runner Agent
#!/usr/bin/env bash
LSB_RELEASE=$(lsb_release -rs)
# Install Microsoft repository
wget https://packages.microsoft.com/config/ubuntu/$LSB_RELEASE/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.debbrew
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
# Install Microsoft GPG public key
curl -L https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
# update
sudo apt-get -yq update
sudo apt-get -yq dist-upgrade
# Check to see if docker is already installed
docker_package=moby
echo "Determing if Docker ($docker_package) is installed"
if ! IsPackageInstalled $docker_package; then
echo "Docker ($docker_package) was not found. Installing..."
sudo apt-get remove -y moby-engine moby-cli
sudo apt-get update
sudo apt-get install -y moby-engine moby-cli
sudo apt-get install --no-install-recommends -y moby-buildx
sudo apt-get install -y moby-compose
else
echo "Docker ($docker_package) is already installed"
fi
# Enable docker.service
sudo systemctl is-active --quiet docker.service || sudo systemctl start docker.service
sudo systemctl is-enabled --quiet docker.service || sudo systemctl enable docker.service
# Docker daemon takes time to come up after installing
sleep 10
docker info
# Always attempt to logout so we do not leave our credentials on the built
# image. Logout _should_ return a zero exit code even if no credentials were
# stored from earlier.
docker logout
###########################################################################################################################################
USER="actions"
REPO="runner"
OS="linux"
ARCH="x64"
PACKAGE="tar.gz"
ACTIONS_URL="https://github.com/libre-devops/azdo-agent-scale-sets"
TOKEN="blah"
runnerLatestAgentVersion="$(curl --silent "https://api.github.com/repos/${USER}/${REPO}/releases/latest" | jq -r .tag_name)"
strippedTagRunnerAgentVersion="$(echo "${runnerLatestAgentVersion}" | sed 's/v//')" && \
runnerPackageUrl="https://github.com/${USER}/${REPO}/releases/download/${runnerLatestAgentVersion}/actions-runner-${OS}-${ARCH}-${strippedTagRunnerAgentVersion}.${PACKAGE}"
actionsPackageName="actions-runner-${OS}-${ARCH}-${strippedTagRunnerAgentVersion}.${PACKAGE}"
curl -o "${actionsPackageName}" -L "${runnerPackageUrl}"
tar xzf "${actionsPackageName}" && rm -rf "${actionsPackageName}"
./config.sh --url "${ACTIONS_URL}" --token "${TOKEN}" && \
./run.sh --unattended
Jenkins Dockerfile
FROM docker.io/jenkins/jenkins:lts
ENV NORMAL_USER jenkins
USER root
LABEL org.opencontainers.image.source=https://github.com/libre-devops/azure-terraform-jenkinsfile
ARG DEBIAN_FRONTEND=noninteractive
ENV DEBIAN_FRONTEND=noninteractive
#Install needed packages as well as setup python with args and pip
RUN apt-get update -y && apt-get dist-upgrade -y && apt-get install -y \
apt-transport-https \
bash \
ca-certificates \
curl \
gcc \
git \
sudo \
software-properties-common \
openssh-server \
unzip \
wget \
zip \
zlib1g-dev && \
useradd -m -s /bin/bash linuxbrew && \
usermod -aG sudo linuxbrew && \
mkdir -p /home/linuxbrew/.linuxbrew && \
chown -R linuxbrew: /home/linuxbrew/.linuxbrew && \
wget -q https://packages.microsoft.com/config/debian/$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release | tr -d '"')/packages-microsoft-prod.deb && \
dpkg -i packages-microsoft-prod.deb && \
apt-get update && \
apt-get install -y powershell
#Set User Path with expected paths for new packages
ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/usr/local/go:/usr/local/go/dev/bin:/usr/local/bin/python3:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.local/bin:/var/jenkins_home:${PATH}"
RUN echo $PATH | tee /etc/environment
USER linuxbrew
RUN /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && \
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/linuxbrew/.bash_profile && \
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/linuxbrew/.bashrc && \
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
RUN brew install tfsec python3 tfenv tree
RUN pip3 install terraform-compliance checkov azure-cli && \
tfenv install latest
RUN echo 'alias powershell="pwsh"' >> ~/.bashrc
USER root
RUN chown -R ${NORMAL_USER} $(brew --prefix)/*
USER ${NORMAL_USER}
RUN echo 'alias powershell="pwsh"' >> ~/.bashrc
Docker-Compose
Podman
Fix WSL Shared mount in podman
wsl.exe -e sh -c 'echo "wsl.exe -u root -e mount --make-rshared /" | sudo tee /etc/profile.d/02-shared-root.sh > /dev/null && sudo chmod +x /etc/profile.d/02-shared-root.sh'
HTTP Reverse Proxy with Podman endpoints
events
{
worker_connections 4096;
}
http
{
upstream gitea
{
server 127.0.0.1:3000 fail_timeout=0;
}
upstream jenkins
{
server 127.0.0.1:8080 fail_timeout=0;
}
upstream nexus
{
server 127.0.0.1:8081 fail_timeout=0;
}
server
{
listen 80;
server_name gitea.libredevops.org;
location /
{
proxy_pass http://gitea;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
server
{
listen 80;
server_name jenkins.libredevops.org;
location /
{
proxy_pass http://jenkins;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_request_buffering off;
# Required for HTTP-based CLI to work over SSL
proxy_buffering off;
# workaround for https://issues.jenkins-ci.org/browse/JENKINS-45651
add_header 'X-SSH-Endpoint' 'jenkins.libredevops.org:50022' always;
}
}
server
{
listen 80;
server_name nexus.libredevops.org;
location /
{
proxy_pass http://nexus;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}
Podman Pod Create
#!/usr/bin/env bash
set -xe
POD_NAME="services"
podman pod create "${POD_NAME}"
#-p 1222:22/tcp \
#50001:50001/tcp \
#8080:8080/tcp \
#3000:3000/tcp \
#3222:22/tcp \
#5432:5432/tcp \
#8081:8081/tcp
podman create \
--name=jenkins \
--pod ${POD_NAME} \
-e ACCEPT_EULA=Y \
-e JENKINS_SLAVE_AGENT_PORT=50001 \
-v craig-workspace_jenkins_home:/var/jenkins_home \
--restart unless-stopped \
"docker.io/jenkins/jenkins:latest" && \
podman create \
--name=gitea \
--pod ${POD_NAME} \
-e USER_UID=1000 \
-e USER_GID=1000 \
-e APP_NAME=self-hosted \
-e REQUIRE_SIGNIN_VIEW=false \
-v gitea:/data \
-v /etc/localtime:/etc/localtime:ro \
--restart unless-stopped \
docker.io/gitea/gitea:latest && \
podman create \
--name=postgres-db \
--pod ${POD_NAME} \
-e POSTGRES_USER=gitea \
-e POSTGRES_PASSWORD=gitea \
-e POSTGRES_DB=gitea \
-v postgres:/var/lib/postgresql/data \
--restart unless-stopped \
docker.io/postgres:latest && \
podman create \
--name=nexus \
--pod ${POD_NAME} \
-v nexus-data:/nexus-data \
--restart unless-stopped \
docker.io/sonatype/nexus3
Kubernetes
Kubernetes Pod Yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: services
name: services
spec:
containers:
- env:
- name: ACCEPT_EULA
value: "Y"
- name: JENKINS_SLAVE_AGENT_PORT
value: "50001"
image: docker.io/jenkins/jenkins:latest
ports:
- containerPort: 8080
- containerPort: 1222
- containerPort: 50001
name: jenkins
resources: {}
securityContext:
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
volumeMounts:
- mountPath: /var/jenkins_home
name: jenkins_home-pvc
- args:
- /bin/s6-svscan
- /etc/s6
env:
- name: REQUIRE_SIGNIN_VIEW
value: "false"
- name: USER_UID
value: "1000"
- name: USER_GID
value: "1000"
- name: APP_NAME
value: self-hosted
image: docker.io/gitea/gitea:latest
ports:
- containerPort: 3000
- containerPort: 3222
name: gitea
resources: {}
securityContext:
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
volumeMounts:
- mountPath: /etc/localtime
name: etc-localtime-host-0
readOnly: true
- mountPath: /data
name: gitea-pvc
- args:
- postgres
env:
- name: POSTGRES_PASSWORD
value: gitea
- name: POSTGRES_DB
value: gitea
- name: POSTGRES_USER
value: gitea
image: docker.io/library/postgres:latest
ports:
- containerPort: 5432
name: postgres-db
resources: {}
securityContext:
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgres-pvc
- image: docker.io/sonatype/nexus3:latest
ports:
- containerPort: 8081
name: nexus
resources: {}
securityContext:
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
volumeMounts:
- mountPath: /nexus-data
name: nexus-data-pvc
hostname: services
restartPolicy: Never
volumes:
- name: jenkins_home-pvc
persistentVolumeClaim:
claimName: jenkins_home
- hostPath:
path: /etc/localtime
type: File
name: etc-localtime-host-0
- name: gitea-pvc
persistentVolumeClaim:
claimName: gitea
- name: postgres-pvc
persistentVolumeClaim:
claimName: postgres
- name: nexus-data-pvc
persistentVolumeClaim:
claimName: nexus-data
status: {}
Install various SDKs on Ubuntu
#Use supplier image
FROM docker.io/ubuntu:latest
RUN rm -rf /bin/sh && ln -sf /bin/bash /bin/sh
LABEL org.opencontainers.image.source=https://github.com/libre-devops/azdo-agent-containers
ARG DEBIAN_FRONTEND=noninteractive
ENV ACCEPT_EULA ${ACCEPT_EULA}
#Set args with blank values - these will be over-written with the CLI
ARG AZP_URL=https://dev.azure.com/Example
ARG AZP_TOKEN=ExamplePatToken
ARG AZP_AGENT_NAME=Example
ARG AZP_POOL=PoolName
ARG AZP_WORK=_work
ARG NORMAL_USER=azp
ARG USER_PASSWORD=azp
#Set the environment with the CLI-passed arguements
ENV AZP_URL ${AZP_URL}
ENV AZP_AGENT_NAME ${AZP_AGENT_NAME}
ENV AZP_POOL ${AZP_POOL}
ENV AZP_WORK ${AZP_WORK}
ENV NORMAL_USER ${NORMAL_USER}
#Set path vars
ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt:/opt/bin:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.local/bin:/home/${NORMAL_USER}/.pyenv:/home/${NORMAL_USER}/.pyenv/bin:/home/${NORMAL_USER}/.local:/home/${NORMAL_USER}/.tfenv:/home/${NORMAL_USER}/.tfenv/bin:/home/${NORMAL_USER}/.pkenv:/home/${NORMAL_USER}/.pkenv/bin:/home/${NORMAL_USER}/.goenv:/home/${NORMAL_USER}/.goenv/bin:/home/${NORMAL_USER}/.jenv:/home/${NORMAL_USER}/.jenv/bin:/home/${NORMAL_USER}/.nvm:/home/${NORMAL_USER}/.rbenv:/home/${NORMAL_USER}/.rbenv/bin:/home/${NORMAL_USER}/.sdkman:/home/${NORMAL_USER}/.sdkman/bin:/home/${NORMAL_USER}/.dotnet:/home/${NORMAL_USER}/.cargo:/home/${NORMAL_USER}/.cargo/bin:/home/${NORMAL_USER}/.phpenv:/home/${NORMAL_USER}/.phpenv/bin"
ENV PATHVAR="PATH=${PATH}"
ENV JAVA_HOME="/home/${NORMAL_USER}/.sdkman/candidates/java/current/bin/java"
ENV NVM_DIR="/home/${NORMAL_USER}/.nvm"
#Declare user expectation, I am performing root actions, so use root.
USER root
RUN mkdir -p /azp && \
useradd -ms /bin/bash ${NORMAL_USER} && \
usermod -aG sudo ${NORMAL_USER} && \
echo "${NORMAL_USER}:${USER_PASSWORD}" | chpasswd && \
chown -R ${NORMAL_USER} /azp && \
mkdir -p /home/linuxbrew && \
chown -R ${NORMAL_USER} /home/linuxbrew && \
apt-get update -y && apt-get dist-upgrade -y && apt-get install -y \
apt-transport-https \
bash \
build-essential \
ca-certificates \
curl \
gcc \
git \
gnupg \
gnupg2 \
jq \
libbz2-dev \
libcurl4-gnutls-dev \
libffi-dev \
libjpeg-dev \
liblzma-dev \
libonig-dev \
libpng-dev \
libreadline-dev \
libsqlite3-dev \
libssl-dev \
libtidy-dev \
libxml2-dev \
libyaml-dev \
libzip-dev \
make \
openssl \
pkg-config \
python-is-python3 \
python3-pip \
python3-tk \
python3-venv \
sudo \
unzip \
wget \
zip \
zlib1g-dev && \
curl -sSLo packages-microsoft-prod.deb https://packages.microsoft.com/config/ubuntu/$(grep -oP '(?<=^DISTRIB_RELEASE=).+' /etc/lsb-release | tr -d '"')/packages-microsoft-prod.deb && \
dpkg -i packages-microsoft-prod.deb && \
rm -rf packages-microsoft-prod.deb && \
apt-get update && \
apt-get install -y powershell && \
ln -sf /bin/pwsh /bin/powershell && \
apt-get update && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
echo $PATHVAR > /etc/environment
USER ${NORMAL_USER}
WORKDIR /home/${NORMAL_USER}
# Install homebrew and gcc per recomendation
RUN echo -en "\n" | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && \
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/${NORMAL_USER}/.bashrc && \
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" && \
brew install gcc
# Install Python
RUN git clone https://github.com/pyenv/pyenv.git ~/.pyenv && \
# Fetch the latest stable version number of Python from python.org
PYTHON_LATEST_VERSION=$(curl -s https://www.python.org/downloads/ | grep -oP 'Download Python \K[0-9.]+(?=<)' | head -n 1) && \
PYTHON_MAJOR_VERSION=$(echo "$PYTHON_LATEST_VERSION" | cut -d '.' -f 1) && \
PYTHON_MINOR_VERSION=$(echo "$PYTHON_LATEST_VERSION" | cut -d '.' -f 2) && \
PYTHON_VERSION="${PYTHON_MAJOR_VERSION}.${PYTHON_MINOR_VERSION}" && \
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc && \
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc && \
echo 'eval "$(pyenv init -)"' >> ~/.bashrc && \
echo 'export PATH=$HOME/.local:$PATH' >> ~/.bashrc && \
source ~/.bashrc && \
pyenv install ${PYTHON_VERSION} && \
pyenv global ${PYTHON_VERSION}
# Install Terraform env
RUN git clone --depth=1 https://github.com/tfutils/tfenv.git ~/.tfenv && \
echo 'export PATH=${HOME}/.tfenv/bin:${PATH}' >> ~/.bashrc && \
source ~/.bashrc && \
tfenv install latest && \
tfenv use latest
# Install Packer Env
RUN git clone https://github.com/iamhsa/pkenv.git ~/.pkenv && \
echo 'export PATH="${HOME}/.pkenv/bin:${PATH}"' >> ~/.bashrc && \
PACKER_LATEST_URL=$(curl -sL https://releases.hashicorp.com/packer/index.json | jq -r '.versions[].builds[].url' | egrep -v 'rc|beta|alpha' | egrep 'linux.*amd64' | tail -1) && \
PACKER_LATEST_VERSION=$(echo "$PACKER_LATEST_URL" | awk -F '/' '{print $6}' | sed 's/packer_//' | sed 's/_linux_amd64.zip//') && \
source ~/.bashrc && \
pkenv install ${PACKER_LATEST_VERSION} && \
pkenv use ${PACKER_LATEST_VERSION}
# Install Ruby
RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv && \
echo 'eval "$(~/.rbenv/bin/rbenv init - bash)"' >> ~/.bashrc && \
source ~/.bashrc && \
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build && \
RUBY_LATEST_VERSION=$(curl -s https://www.ruby-lang.org/en/downloads/ | grep -oP 'The current stable version is \K[0-9.]+' | sed 's/\.$//') && \
rbenv install ${RUBY_LATEST_VERSION} && \
rbenv global ${RUBY_LATEST_VERSION}
# Install Go
RUN git clone https://github.com/syndbg/goenv.git ~/.goenv && \
echo 'export GOENV_ROOT="${HOME}/.goenv"' >> ~/.bashrc && \
echo 'export PATH="${GOENV_ROOT}/bin:${PATH}"' >> ~/.bashrc && \
echo 'eval "$(goenv init -)"' >> ~/.bashrc && \
echo 'export PATH="${GOROOT}/bin:${PATH}"' >> ~/.bashrc && \
echo 'export PATH="${PATH}:${GOPATH}/bin"' >> ~/.bashrc && \
source ~/.bashrc && \
GO_LATEST_VERSION=$(goenv install -l | grep -E -o '[0-9]+\.[0-9]+(\.[0-9]+)?' | tail -1) && \
goenv install ${GO_LATEST_VERSION} && \
goenv global ${GO_LATEST_VERSION}
# Install Dotnet SDK non-interactively
RUN curl -L dotnet-install.sh https://dot.net/v1/dotnet-install.sh | bash
# Install Rust non-interactively
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y
# Install NVM and Node.JS LTS
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash && \
source ~/.bashrc && \
nvm install --lts && \
nvm use --lts \
# Install SDKMAN! since all Java venvs are annoying
RUN curl -s "https://get.sdkman.io" | bash && \
source "$HOME/.sdkman/bin/sdkman-init.sh" && \
MICROSOFT_LATEST_OPENJDK=$(sdk list java | grep ms | grep -E -o '[0-9]+\.[0-9]+\.[0-9]+-ms' | head -1) && \
sdk install java ${MICROSOFT_LATEST_OPENJDK} && \
sdk use java ${MICROSOFT_LATEST_OPENJDK}
COPY start.sh /home/${NORMAL_USER}/start.sh
RUN chmod 755 start.sh
CMD [ "./start.sh" ]
Random Docker-Compose
---
version: "3.8"
services:
rdesktop:
image: ghcr.io/libre-devops/gui-tooling-container:latest
container_name: tooling-container
privileged: true
volumes:
- dev-container-home:/home
- dev-container-opt:/opt
environment:
- TZ=Europe/London
ports:
- 3389:3389
shm_size: "1gb" #optional
restart: unless-stopped
volumes:
dev-container-home:
dev-container-opt:
Source: docs/cheatsheets/containers-cheatsheet.md