Pencil is a simple service-registration tool for Docker and uses Consul as a backend for
Service-Discovery. It syncronizes only the delta ∆
between the local state on every node
(Running Docker Containers) and the remote state on Consul registry every (n) seconds.
The default is set to 10s
.
Pencil have been used in many different production systems and successfully synchronized hundreds of thouthands of contianers without problems.
docker run -it -v /var/run/docker.sock:/var/run/docker.sock alaa/pencil:2 <consul-registry-address>
Or build the docker image yourself:
Build the Docker image
docker build -t pencil .
Run it
docker run -d \
-v /var/run/docker.sock:/tmp/docker.sock \
pencil <consul-registry-address>
we need to mount the docker-engine socket into Pencil container in order to give it a privilige to observe the contaners state on the host.
You can pass array of strings to Consul Tags using container environment variables.
-
All tags should start with
SERVICE_
-
All tags keys should be passed as upper case letters as
SERVICE_TAG
notSERVICE_tag
-
Tag keys should match the following regex:
/^SERVICE_[A-Z0-9-_]+[A-Z0-9]$/
-
Tag keys should be greater than
5 chars
and less than40 chars
-
Tag values should match the following regex:
/^[a-z0-9-_]+[a-z0-9]$/
-
Tag values should be greater than
3 chars
and less than200 chars
For example: docker run -P -e "SERVICE_CLUSTER=staging_01" nginx
By default Pencil registers every docker contianer that exposes a TCP Port
. and uses the
following convention for registering services on consul: <docker-image-name>-<exposed-container-port>
.
For example: docker run -P nginx
will let Pencil to register this container under the following name:
nginx-80
as nginx image exposes TCP port 80
If you wish to register your service as an alternative name, you can pass SERVICE_NAME
to the container
and Pencil will use that string as the preferred name
for consul registration.
For example: docker run -P -e 'SERVICE_NAME=testing-microservice' nginx
You can pass custom health check to Consul via docker container environment variables.
Pencil fills out the host
and port
dynamically on the run time. For example:
docker run -P nginx -e "SERVICE_HEALTH_CHECK='curl -Ss http://%<host>s:%<port>s/health'"
docker run -P nginx -e "SERVICE_HEALTH_CHECK='nc -vz %<host>s %<port>s'"
Pencil replaces the host
with the value of <consul-registry-address>
passed to the agent
and replaces the port
dynamically by inspecting container.PortMapping
The following example uses SERVICE_
lables in a way to build nginx ingress rules:
I will exaplain here the lables I choose to use for this particular example:
SERVICE_SCOPE
: If I want to register only public services on this ingress or only the internal services.
SERVICE_TYPE
: If I want to configure HTTP or TCP or WebSockets in a specific blocks.
SERVICE_NAME
: The service name I choose to use as sub-domain on the ingress config: i.e: nginx virtual host
SERVICE_ACL
: If I want to pass service Access List, especeially if I am exposing or registering this services on a public nginx ingress.
@upstream_domain
and @server_name
: These are just variables can be configured to set the domain name you wish to expose your ingress on.
You can replace these variavles starting with @
to your desired static values.
{{ range services }}
{{ $tags := .Tags | join "," }}
{{ if $tags | regexMatch "SERVICE_SCOPE=public" }}
{{ if $tags | regexMatch "SERVICE_TYPE=(http)" }}
{{ $services := service .Name }}
{{ $len := len $services }}
{{ if gt $len 0 }}
upstream {{.Name}}.<%=@upstream_domain%> {
{{ range service .Name }}
server {{.Address}}:{{.Port}};
{{end}}
}
{{end}}
{{ end }}
{{ end }}
{{ end }}
{{ range services }}
{{ $tags := .Tags | join "," }}
{{ if $tags | regexMatch "SERVICE_SCOPE=public" }}
{{ if $tags | regexMatch "SERVICE_TYPE=(http)" }}
{{ $services := service .Name }}
{{ $len := len $services }}
{{ if gt $len 0 }}
server {
server_name <%=@server_names%>;
# Fetch Service ACL
{{range $tag := .Tags}}
{{ if $tag | regexMatch "SERVICE_ACL" }}
{{ $acls := (index ($tag | split "=") 1) | split "," }}
{{ range $acls }} allow {{ . }}; {{ end }}
deny all;
{{end}}
{{end}}
location / {
proxy_pass http://{{.Name}}.<%=@upstream_domain%>;
}
}
{{end}}
{{ end }}
{{ end }}
{{ end }}
- Refactoring
- Write tests