Successfully run webserver from Docker image

This commit is contained in:
Radon Rosborough 2020-12-25 19:34:15 -08:00
parent 938e41b1c6
commit 2e6aafbcb3
15 changed files with 325 additions and 17 deletions

View File

@ -28,6 +28,8 @@ image:
@: $${I}
ifeq ($(I),composite)
node tools/build-composite-image.js
else ifeq ($(I),app)
docker build . -f docker/$(I)/Dockerfile -t riju:$(I)
else
docker build . -f docker/$(I)/Dockerfile -t riju:$(I) --pull
endif
@ -51,13 +53,24 @@ pkg:
VOLUME_MOUNT ?= $(PWD)
P1 ?= 6119
P2 ?= 6120
ifneq (,$(E))
SHELL_PORTS := -p 127.0.0.1:$(P1):6119 -p 127.0.0.1:$(P2):6120
else
SHELL_PORTS :=
endif
.PHONY: shell
shell:
@: $${I}
ifeq ($(I),admin)
docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -v /var/run/docker.sock:/var/run/docker.sock -v $(HOME)/.aws:/var/riju/.aws:ro -e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e VOLUME_MOUNT=$(VOLUME_MOUNT) --network host riju:$(I)
docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -v /var/run/docker.sock:/var/run/docker.sock -v $(HOME)/.aws:/var/riju/.aws -v $(HOME)/.docker:/var/riju/.docker -v $(HOME)/.terraform.d:/var/riju/.terraform.d -e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e VOLUME_MOUNT=$(VOLUME_MOUNT) $(SHELL_PORTS) --network host riju:$(I)
else ifeq ($(I),compile)
docker run -it --rm --hostname $(I) $(SHELL_PORTS) riju:$(I)
else
docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -p 127.0.0.1:6119:6119 -p 127.0.0.1:6120:6120 riju:$(I)
docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src $(SHELL_PORTS) riju:$(I)
endif
.PHONY: install
@ -99,8 +112,6 @@ build: frontend system
dev:
make -j3 frontend-dev system-dev server-dev
### Run application code
### Fetch artifacts from registries
.PHONY: pull

View File

@ -47,7 +47,7 @@ app.get("/:lang", (req, res) => {
analyticsEnabled,
});
} else {
res.send(`No such language: ${lang}`);
res.send(404, `No such language: ${lang}\n`);
}
});
app.use("/css", express.static("frontend/styles"));

View File

@ -11,6 +11,7 @@ apt-get update
apt-get install -y curl gnupg lsb-release
curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
@ -21,13 +22,33 @@ ubuntu_name="$(lsb_release -cs)"
node_repo="$(curl -sS https://deb.nodesource.com/setup_current.x | grep NODEREPO= | grep -Eo 'node_[0-9]+\.x' | head -n1)"
tee -a /etc/apt/sources.list.d/custom.list >/dev/null <<EOF
deb [arch=amd64] https://apt.releases.hashicorp.com ${ubuntu_name} main
deb [arch=amd64] https://deb.nodesource.com/${node_repo} ${ubuntu_name} main
deb [arch=amd64] https://dl.yarnpkg.com/debian/ stable main
deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable
deb [arch=amd64] https://download.docker.com/linux/ubuntu ${ubuntu_name} stable
EOF
packages="
docker-ce-cli
jq
less
make
man
nodejs
packer
sudo
tmux
terraform
unzip
vim
wget
yarn
"
apt-get update
apt-get install -y docker-ce-cli less make man nodejs sudo tmux unzip wget yarn
apt-get install -y $(sed 's/#.*//' <<< "${packages}")
wget https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -O awscli.zip
unzip awscli.zip

View File

@ -7,6 +7,8 @@ useradd -u "$(stat -c %u "$PWD")" -g "$(stat -c %g "$PWD")" -o -p '!' -m -N -l -
runuser -u riju -- touch /home/riju/.sudo_as_admin_successful
runuser -u riju -- ln -sT /var/riju/.aws /home/riju/.aws
runuser -u riju -- ln -sT /var/riju/.docker /home/riju/.docker
runuser -u riju -- ln -sT /var/riju/.terraform.d /home/riju/.terraform.d
runuser -u riju -- yarn install
exec runuser -u riju "$@"

View File

@ -1,11 +1,13 @@
FROM riju:compile AS compile
FROM riju:composite
ENTRYPOINT ["/usr/local/sbin/my_init"]
RUN useradd -p '!' -m -l -s /usr/bin/bash riju
WORKDIR /src
COPY --chown=riju:riju --from=compile . ./
COPY --chown=riju:riju --from=compile /src ./
RUN chown root:riju system/out/*-privileged && chmod a=,g=rx,u=rwxs system/out/*-privileged
USER riju
CMD ["make", "server"]

View File

@ -1,9 +1,13 @@
FROM ubuntu:rolling AS build
FROM ubuntu:rolling
COPY docker/runtime/install.bash /tmp/
COPY docker/compile/install.bash /tmp/
RUN /tmp/install.bash
WORKDIR /src
COPY Makefile ./
COPY system ./system/
RUN make system
COPY package.json yarn.lock ./
RUN yarn install
@ -12,9 +16,6 @@ COPY webpack.config.cjs ./
COPY frontend/src ./frontend/src/
RUN make frontend
COPY system ./system/
RUN make system
COPY frontend/pages ./frontend/pages/
COPY frontend/styles ./frontend/styles/
COPY backend ./backend/

26
docker/compile/install.bash Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euxo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get dist-upgrade -y
apt-get install -y curl gnupg lsb-release
curl -sSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
ubuntu_ver="$(lsb_release -rs)"
ubuntu_name="$(lsb_release -cs)"
node_repo="$(curl -sS https://deb.nodesource.com/setup_current.x | grep NODEREPO= | grep -Eo 'node_[0-9]+\.x' | head -n1)"
tee -a /etc/apt/sources.list.d/custom.list >/dev/null <<EOF
deb https://deb.nodesource.com/${node_repo} ${ubuntu_name} main
deb https://dl.yarnpkg.com/debian/ stable main
EOF
apt-get update
apt-get install -y clang g++ make nodejs sudo yarn

54
packer/provision.bash Normal file
View File

@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get dist-upgrade
apt-get install -y curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
ubuntu_name="$(lsb_release -cs)"
tee -a /etc/apt/sources.list.d/custom.list >/dev/null <<EOF
deb [arch=amd64] https://download.docker.com/linux/ubuntu ${ubuntu_name} stable
EOF
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io
sed -i "s#DOCKER_REPO_REPLACED_BY_PACKER#${DOCKER_REPO}#" /usr/local/bin/riju-deploy
for user in admin deploy; do
if ! grep -vq "PRIVATE KEY" "/tmp/id_${user}.pub"; then
echo "${user} public key was set to a private key, aborting" >&2
exit 1
fi
IFS=" " read contents < "/tmp/id_${user}.pub"
echo "${contents}" > "/tmp/id_${user}.pub"
done
sed -Ei 's/^#?PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -Ei 's/^#?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -Ei 's/^#?PermitEmptyPasswords .*/PermitEmptyPasswords no/' /etc/ssh/sshd_config
passwd -l root
useradd admin -g admin -G sudo -s /usr/bin/bash -p "$(echo "${ADMIN_PASSWORD}" | mkpasswd -s)" -m
useradd deploy -s /usr/bin/bash -p "!"
for user in admin deploy; do
mkdir -p "/home/${user}/.ssh"
mv "/tmp/id_${user}.pub" "/home/${user}/.ssh/authorized_keys"
chown -R "${user}:${user}" "/home/${user}/.ssh"
chmod -R go-rwx "/home/${user}/.ssh"
done
sed -i 's/^/command="sudo riju-deploy",restrict/' /home/deploy/.ssh/authorized_keys
cat <<"EOF" > /etc/sudoers.d/riju
deploy ALL=(root) NOPASSWD: /usr/local/bin/riju-deploy
EOF

36
packer/riju Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
domain="$(ls /etc/letsencrypt/live | grep -v README | head -n1)"
if [[ -n "${domain}" ]]; then
echo "Detected cert for domain: ${domain}, enabling TLS" >&2
export TLS=1
TLS_PRIVATE_KEY="$(base64 "/etc/letsencrypt/live/${domain}/privkey.pem")"
TLS_CERTIFICATE="$(base64 "/etc/letsencrypt/live/${domain}/fullchain.pem")"
export TLS_PRIVATE_KEY TLS_CERTIFICATE
if [[ "${domain}" == riju.codes ]]; then
echo "Domain is riju.codes, enabling analytics" >&2
export ANALYTICS=1
else
echo "Domain is not riju.codes, disabling analytics" >&2
fi
else
echo "No certs installed in /etc/letsencrypt/live, disabling TLS" >&2
fi
if [[ -n "${DETACH:-}" ]]; then
extra_args="-d"
elif [[ -t 1 ]]; then
extra_args="-it"
else
extra_args=
fi
port_args="${PORT_MAPPING:--p 0.0.0.0:80:6119 -p 0.0.0.0:443:6120}"
docker run --rm ${port_args} ${extra_args} \
-e TLS -e TLS_PRIVATE_KEY -e TLS_CERTIFICATE -e ANALYTICS \
-h riju --name "${CONTAINER_NAME:-riju-prod}" \
"${IMAGE_NAME}:-riju:app"

41
packer/riju-deploy Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
DOCKER_REPO="${DOCKER_REPO:-DOCKER_REPO_REPLACED_BY_PACKER}"
if (( $# != 1 )); then
echo "usage: ssh deploy@riju COMMIT-SHA" >&2
exit 1
fi
commit="$1"
if [[ "$(echo -n "${commit}" | wc -c)" != 40 ]]; then
echo "riju-deploy: invalid commit SHA: ${commit}" >&2
exit 1
fi
image="${DOCKER_REPO}:app-${commit}"
echo "Pull image to be deployed..."
docker pull "${image}"
echo "Start new image in test container..." >&2
CONTAINER_NAME=riju-test IMAGE_NAME="${image}" DETACH=1 \
PORT_MAPPING="-p 127.0.0.1:6119:6119" riju
echo "Wait for web server to come up..." >&2
sleep 10
echo "Test web server health..." >&2
curl -fsSL http://localhost:6119 | head -n15
echo "Tear down test container..." >&2
docker stop riju-test
echo "Retag production image..." >&2
docker tag "${image}" riju:app
echo "Restart production server..." >&2
systemctl restart riju

12
packer/riju.service Normal file
View File

@ -0,0 +1,12 @@
[Unit]
Description=Riju online coding sandbox
Requires=docker.service
After=docker.service
[Service]
Type=exec
ExecStart=riju
Restart=always
[Install]
WantedBy=multi-user.target

70
packer/server.json Normal file
View File

@ -0,0 +1,70 @@
{
"variables": {
"docker_repo": "{{env `DOCKER_REPO`}}",
"admin_password": "{{env `ADMIN_PASSWORD`}}",
"admin_ssh_public_key_file": "{{env `ADMIN_SSH_PUBLIC_KEY_FILE`}",
"deploy_ssh_public_key_file": "{{env `DEPLOY_SSH_PUBLIC_KEY_FILE`}"
},
"builders": [
{
"type": "amazon-ebs",
"source_ami_filter": {
"filters": {
"virtualization-type": "hvm",
"root-device-type": "ebs",
"name": "ubuntu/images/ubuntu-groovy-20.10-amd64-server-*"
},
"owners": ["099720109477"],
"most_recent": true
},
"instance_type": "t3.micro",
"ssh_username": "ubuntu",
"ami_name": "riju-{{timestamp}}"
}
],
"provisioners": [
{
"type": "shell",
"script": "validate.bash",
"environment_vars": [
"DOCKER_REPO={{user `docker_repo`}}",
"ADMIN_PASSWORD={{user `admin_password`}}",
"ADMIN_SSH_PUBLIC_KEY_FILE={{user `admin_ssh_public_key_file`}}",
"DEPLOY_SSH_PUBLIC_KEY_FILE={{user `deploy_ssh_public_key_file`}}"
]
},
{
"type": "file",
"source": "riju",
"destination": "/usr/local/bin/riju"
},
{
"type": "file",
"source": "riju-deploy",
"destination": "/usr/local/bin/riju-deploy"
},
{
"type": "file",
"source": "riju.service",
"destination": "/etc/systemd/system/riju.service"
},
{
"type": "file",
"source": "{{user `admin_ssh_public_key_file`}}",
"destination": "/tmp/id_admin.pub"
},
{
"type": "file",
"source": "{{user `deploy_ssh_public_key_file`}}",
"destination": "/tmp/id_deploy.pub"
},
{
"type": "shell",
"script": "provision.bash",
"environment_vars": [
"DOCKER_REPO={{user `docker_repo`}}",
"ADMIN_PASSWORD={{user `admin_password`}}"
]
}
]
}

8
packer/validate.bash Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
: ${DOCKER_REPO}
: ${ADMIN_PASSWORD}
: ${ADMIN_SSH_PUBLIC_KEY_FILE}
: ${DEPLOY_SSH_PUBLIC_KEY_FILE}

View File

@ -19,7 +19,9 @@ for src in system/src/*.c; do
out="${out/.c}"
verbosely clang -Wall -Wextra -Werror -std=c11 "${src}" -o "${out}"
if [[ "${out}" == *-privileged ]]; then
sudo chown root:riju "${out}"
if getent group riju >/dev/null; then
sudo chown root:riju "${out}"
fi
sudo chmod a=,g=rx,u=rwxs "${out}"
fi
done

View File

@ -13,15 +13,37 @@ terraform {
}
}
locals {
tags = {
Terraform = "Managed by Terraform"
}
}
data "external" "env" {
program = ["jq", "-n", "env"]
}
provider "aws" {
profile = "default"
region = "us-west-1"
}
data "aws_region" "current" {}
resource "aws_s3_bucket" "riju_debs" {
bucket = "riju-debs"
acl = "public-read"
tags = {
Terraform = "Managed by Terraform"
}
tags = local.tags
}
resource "aws_instance" "server" {
instance_type = "t3.micro"
ami = data.external.env.result.AMI_ID
tags = local.tags
}
resource "aws_ebs_volume" "data" {
availability_zone = "${data.aws_region.current.name}a"
size = 100
tags = local.tags
}