Intro
You are tasked to create a build environment and you want something relatively easy and something that is repeatable.
On my journey to accomplish the above task i came across many great solutions. There are all in one solutions like Gitlab and Github but i wanted a solution that was open-source, free and easy.
Gitea
Gitea
Gitea is a open-source version of something like Github that is easy to deploy and makes a great on-prem repository. An added advantage is it has out of the box integration with Drone that is configurable to trigger web-hooks on selected events.
Drone
Drone
Drone is a open-source CI tool that will take simple commands in the form of yaml and execute on them as a pipeline. They have a concept of runners that do all the work. You install the runners you need on Docker, or Kubernetes or even on a host and they will listen for jobs.
Runners:
Docker
Kubernetes
Exec
SSH
VM
Portainer
Portainer
Portainer is a container management platform that allows you to manage containers across clouds and allows you to manage multiple container platforms (Kubernetes, Docker, Docker Swarm, and Nomad)
Solution:
I am using VSCode as my IDE in which i have created a little python webserver that will be encapsulated into a docker image. The Docker image will then be created, sent to a repo and then consumed by a Kubernetes deployment. The deployment will be triggered and deployed.
Here is what it will look like:
[VSCode] -> [Gitea] -> [Drone] -> [Portainer] -> [Deploy]
Gitea Install:
Kubernetes:
helm install gitea gitea-charts/gitea -n gitea --values ./gitea_values.yml
gitea-values.yml (Changes)
ingress:
enabled: true
# className: nginx
className:
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: git.chenetz.net
paths:
- path: /
pathType: Prefix
tls:
- secretName: git-chenetz-tls
hosts:
- git.chenetz.net
Drone Install:
Kubernetes:
helm upgrade my-drone drone/drone -n drone --values ./drone-values.yaml
drone-values.yaml (changes)
ingress:
enabled: true
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: drone.chenetz.net
paths:
- path: /
pathType: Prefix
tls:
- secretName: chenetz-drone-tls
hosts:
- drone.chenetz.net
nv:
DRONE_WEBHOOK_SKIP_VERIFY: true
## REQUIRED: Set the user-visible Drone hostname, sans protocol.
## Ref: https://docs.drone.io/installation/reference/drone-server-host/
##
DRONE_SERVER_HOST: drone.chenetz.net
## The protocol to pair with the value in DRONE_SERVER_HOST (http or https).
## Ref: https://docs.drone.io/installation/reference/drone-server-proto/
##
DRONE_SERVER_PROTO: https
## REQUIRED: Set the secret secret token that the Drone server and its Runners will use
## to authenticate. This is commented out in order to leave you the ability to set the
## key via a separately provisioned secret (see existingSecretName above).
## Ref: https://docs.drone.io/installation/reference/drone-rpc-secret/
##
DRONE_RPC_SECRET: drone-runner
## If you'd like to use a DB other than SQLite (the default), set a driver + DSN here.
## Ref: https://docs.drone.io/installation/storage/database/
##
# DRONE_DATABASE_DRIVER:
# DRONE_DATABASE_DATASOURCE:
## If you are going to store build secrets in the Drone database, it is suggested that
## you set a database encryption secret. This must be set before any secrets are stored
## in the database.
## Ref: https://docs.drone.io/installation/storage/encryption/
##
# DRONE_DATABASE_SECRET:
## If you are using self-hosted GitHub or GitLab, you'll need to set this to true.
## Ref: https://docs.drone.io/installation/reference/drone-git-always-auth/
##
# DRONE_GIT_ALWAYS_AUTH: false
## ===================================================================================
## Provider Directives (select ONE)
## -----------------------------------------------------------------------------------
## Select one provider (and only one). Refer to the corresponding documentation link
## before filling the values in. Also note that you can use the 'secretMounts' value
## if you'd rather not have secrets in Kubernetes Secret instead of a ConfigMap.
## ===================================================================================
## GitHub-specific variables. See the provider docs here:
## Ref: https://docs.drone.io/installation/providers/github/
##
# DRONE_GITHUB_CLIENT_ID:
# DRONE_GITHUB_CLIENT_SECRET:
## GitLab-specific variables. See the provider docs here:
## Ref: https://docs.drone.io/installation/providers/gitlab/
##
# DRONE_GITLAB_CLIENT_ID:
# DRONE_GITLAB_CLIENT_SECRET:
# DRONE_GITLAB_SERVER:
## Bitbucket Cloud-specific variables. See the provider docs here:
## Ref: https://docs.drone.io/installation/providers/bitbucket-cloud/
##
# DRONE_BITBUCKET_CLIENT_ID:
# DRONE_BITBUCKET_CLIENT_SECRET:
## Bitbucket-specific variables. See the provider docs here:
## Ref: https://docs.drone.io/installation/providers/bitbucket-server/
##
# DRONE_GIT_USERNAME:
# DRONE_GIT_PASSWORD:
# DRONE_STASH_CONSUMER_KEY:
# DRONE_STASH_PRIVATE_KEY:
# DRONE_STASH_SERVER:
## Gitea-specific variables. See the provider docs here:
## Ref: https://docs.drone.io/installation/providers/gitea/
##
DRONE_GITEA_CLIENT_ID: [client id]
DRONE_GITEA_CLIENT_SECRET: [secret]
DRONE_GITEA_SERVER: https://git.chenetz.net
Drone Docker Runner Install:
Kubernetes:
helm install my-drone-runner-docker drone/drone-runner-docker -n drone --values ./drone-runner-docker.yml
drone-runner-docker.yml (changes)
env:
## The docker host to be used by the drone-runner-docker when connecting
## to the docker daemon, defaults to the dind(sidecar) docker daemon
DOCKER_HOST: "tcp://localhost:2375"
## REQUIRED: Set the secret secret token that the Docker runner will use
## to authenticate. This is commented out in order to leave you the ability to set the
## key via a separately provisioned secret (see extraSecretNamesForEnvFrom above).
## Ref: https://docs.drone.io/runner/docker/configuration/reference/drone-rpc-secret/
##
DRONE_RPC_SECRET: drone-runner
## The hostname/IP (and optionally the port) for your Kubernetes runner. Defaults to the "drone"
## service that the drone server Chart creates by default.
## Ref: https://docs.drone.io/runner/docker/configuration/reference/drone-rpc-host/
##
DRONE_RPC_HOST: drone.chenetz.net
## The protocol to use for communication with Drone server.
## Ref: https://docs.drone.io/runner/docker/configuration/reference/drone-rpc-proto/
##
DRONE_RPC_PROTO: https
DRONE_NAMESPACE_DEFAULT: drone
Configuration:
Gitea:
Drone
After getting everything up and communicating, i added the .values.yml file to my repo. The following drone pipeline file creates the docker image after it gets a trigger from either a push or pull in Gitea. The second step triggers a webhook that is configured in Portainer to trigger a deployment of the manifest.yml in the repo.
kind: pipeline
name: default
type: docker
steps:
- name: docker
image: plugins/docker
settings:
username:
from_secret: docker-username
password:
from_secret: docker-password
repo: mchenetz/test
tags:
- latest
- name: send
image: plugins/webhook
settings:
urls: https://192.168.10.12:9443/api/stacks/webhooks/
content_type: application/json
template: |
{
"owner": "{{ repo.owner }}",
"repo": "{{ repo.name }}",
"status": "{{ build.status }}",
}
skip_verify: true
debug: true
Dockerfile
FROM python:latest
WORKDIR /src
COPY . .
CMD [ "python", "./test.py"]
Source (test.py)
from http.server import BaseHTTPRequestHandler, HTTPServer
import time
hostName = "localhost"
serverPort = 80
class MyServer(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes("<html><head><title>https://portainer.io</title></head>", "utf-8"))
self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
self.wfile.write(bytes("<body>", "utf-8"))
self.wfile.write(bytes("<p>This is an example web server on k8s.</p>", "utf-8"))
self.wfile.write(bytes("</body></html>", "utf-8"))
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print("Server started http://%s:%s" % (hostName, serverPort))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
Example of automated trigger on push:
Docker Repo:
Portainer Trigger:
Conclusion:
At the end of the day... It may sound like a few steps to setup but i now have a complete environment for my coding pipeline and any changes i make will automatically build the image and deploy. That may not always be what you want and the pipeline can be modified to insert stops for approval and to change to pull which is more common.
Top comments (0)